From b9b9eea0f4656a51843a75af16e004a2061162c4 Mon Sep 17 00:00:00 2001 From: Gordon Tetlow Date: Tue, 14 May 2019 23:48:52 +0000 Subject: [PATCH] Add SA-19:03 to SA-19:07 and EN-19:08 to EN-19:10. Approved by: so --- .../advisories/FreeBSD-EN-19:08.tzdata.asc | 146 + .../advisories/FreeBSD-EN-19:09.xinstall.asc | 128 + .../advisories/FreeBSD-EN-19:10.scp.asc | 125 + .../advisories/FreeBSD-SA-19:03.wpa.asc | 154 + .../advisories/FreeBSD-SA-19:04.ntp.asc | 146 + .../advisories/FreeBSD-SA-19:05.pf.asc | 134 + .../advisories/FreeBSD-SA-19:06.pf.asc | 134 + .../advisories/FreeBSD-SA-19:07.mds.asc | 198 + .../patches/EN-19:08/tzdata-2019a.patch | 590 + .../patches/EN-19:08/tzdata-2019a.patch.asc | 18 + .../security/patches/EN-19:09/xinstall.patch | 71 + .../patches/EN-19:09/xinstall.patch.asc | 18 + share/security/patches/EN-19:10/scp.patch | 462 + share/security/patches/EN-19:10/scp.patch.asc | 18 + share/security/patches/SA-19:03/wpa-11.patch | 192652 +++++++++++++++ .../patches/SA-19:03/wpa-11.patch.asc | 18 + share/security/patches/SA-19:03/wpa-12.patch | 133835 ++++++++++ .../patches/SA-19:03/wpa-12.patch.asc | 18 + .../security/patches/SA-19:04/ntp-11.2.patch | 43036 ++++ .../patches/SA-19:04/ntp-11.2.patch.asc | 18 + share/security/patches/SA-19:04/ntp.patch | 35712 +++ share/security/patches/SA-19:04/ntp.patch.asc | 18 + share/security/patches/SA-19:05/pf.patch | 16 + share/security/patches/SA-19:05/pf.patch.asc | 18 + share/security/patches/SA-19:06/pf.patch | 69 + share/security/patches/SA-19:06/pf.patch.asc | 18 + .../patches/SA-19:07/mds.11-stable.patch | 835 + .../patches/SA-19:07/mds.11-stable.patch.asc | 18 + .../security/patches/SA-19:07/mds.11.2.patch | 849 + .../patches/SA-19:07/mds.11.2.patch.asc | 18 + .../patches/SA-19:07/mds.12-stable.patch | 854 + .../patches/SA-19:07/mds.12-stable.patch.asc | 18 + .../security/patches/SA-19:07/mds.12.0.patch | 868 + .../patches/SA-19:07/mds.12.0.patch.asc | 18 + share/xml/advisories.xml | 30 + share/xml/notices.xml | 21 + 36 files changed, 411299 insertions(+) create mode 100644 share/security/advisories/FreeBSD-EN-19:08.tzdata.asc create mode 100644 share/security/advisories/FreeBSD-EN-19:09.xinstall.asc create mode 100644 share/security/advisories/FreeBSD-EN-19:10.scp.asc create mode 100644 share/security/advisories/FreeBSD-SA-19:03.wpa.asc create mode 100644 share/security/advisories/FreeBSD-SA-19:04.ntp.asc create mode 100644 share/security/advisories/FreeBSD-SA-19:05.pf.asc create mode 100644 share/security/advisories/FreeBSD-SA-19:06.pf.asc create mode 100644 share/security/advisories/FreeBSD-SA-19:07.mds.asc create mode 100644 share/security/patches/EN-19:08/tzdata-2019a.patch create mode 100644 share/security/patches/EN-19:08/tzdata-2019a.patch.asc create mode 100644 share/security/patches/EN-19:09/xinstall.patch create mode 100644 share/security/patches/EN-19:09/xinstall.patch.asc create mode 100644 share/security/patches/EN-19:10/scp.patch create mode 100644 share/security/patches/EN-19:10/scp.patch.asc create mode 100644 share/security/patches/SA-19:03/wpa-11.patch create mode 100644 share/security/patches/SA-19:03/wpa-11.patch.asc create mode 100644 share/security/patches/SA-19:03/wpa-12.patch create mode 100644 share/security/patches/SA-19:03/wpa-12.patch.asc create mode 100644 share/security/patches/SA-19:04/ntp-11.2.patch create mode 100644 share/security/patches/SA-19:04/ntp-11.2.patch.asc create mode 100644 share/security/patches/SA-19:04/ntp.patch create mode 100644 share/security/patches/SA-19:04/ntp.patch.asc create mode 100644 share/security/patches/SA-19:05/pf.patch create mode 100644 share/security/patches/SA-19:05/pf.patch.asc create mode 100644 share/security/patches/SA-19:06/pf.patch create mode 100644 share/security/patches/SA-19:06/pf.patch.asc create mode 100644 share/security/patches/SA-19:07/mds.11-stable.patch create mode 100644 share/security/patches/SA-19:07/mds.11-stable.patch.asc create mode 100644 share/security/patches/SA-19:07/mds.11.2.patch create mode 100644 share/security/patches/SA-19:07/mds.11.2.patch.asc create mode 100644 share/security/patches/SA-19:07/mds.12-stable.patch create mode 100644 share/security/patches/SA-19:07/mds.12-stable.patch.asc create mode 100644 share/security/patches/SA-19:07/mds.12.0.patch create mode 100644 share/security/patches/SA-19:07/mds.12.0.patch.asc diff --git a/share/security/advisories/FreeBSD-EN-19:08.tzdata.asc b/share/security/advisories/FreeBSD-EN-19:08.tzdata.asc new file mode 100644 index 0000000000..a9a2584008 --- /dev/null +++ b/share/security/advisories/FreeBSD-EN-19:08.tzdata.asc @@ -0,0 +1,146 @@ +-----BEGIN PGP SIGNED MESSAGE----- +Hash: SHA512 + +============================================================================= +FreeBSD-EN-19:08.tzdata Errata Notice + The FreeBSD Project + +Topic: Timezone database information update + +Category: contrib +Module: zoneinfo +Announced: 2019-01-09 +Affects: All supported versions of FreeBSD. +Corrected: 2019-03-29 01:39:20 UTC (stable/12, 12.0-STABLE) + 2019-05-14 22:48:36 UTC (releng/12.0, 12.0-RELEASE-p4) + 2019-01-01 01:40:44 UTC (stable/11, 11.3-PRERELEASE) + 2019-05-14 22:48:36 UTC (releng/11.2, 11.2-RELEASE-p10) + +For general information regarding FreeBSD Errata Notices and Security +Advisories, including descriptions of the fields above, security +branches, and the following sections, please visit +. + +I. Background + +The tzsetup(8) program allows the user to specify the default local timezone. +Based on the selected timezone, tzsetup(8) copies one of the files from +/usr/share/zoneinfo to /etc/localtime. This file actually controls the +conversion. + +II. Problem Description + +Several changes in Daylight Savings Time happened after previous FreeBSD +releases were released that would affect many people who live in different +countries. Because of these changes, the data in the zoneinfo files need to +be updated, and if the local timezone on the running system is affected, +tzsetup(8) needs to be run so the /etc/localtime is updated. + +III. Impact + +An incorrect time will be displayed on a system configured to use one of the +affected timezones if the /usr/share/zoneinfo and /etc/localtime files are +not updated, and all applications on the system that rely on the system time, +such as cron(8) and syslog(8), will be affected. + +IV. Workaround + +The system administrator can install an updated timezone database from the +misc/zoneinfo port and run tzsetup(8) to get the timezone database corrected. + +Applications that store and display times in Coordinated Universal Time (UTC) +are not affected. + +V. Solution + +Please note that some third party software, for instance PHP, Ruby, Java and +Perl, may be using different zoneinfo data source, in such cases this +software must be updated separately. For software packages that is installed +via binary packages, they can be upgraded by executing `pkg upgrade'. + +Following the instructions in this Errata Notice will update all of the +zoneinfo files to be the same as what was released with FreeBSD release. + +Perform one of the following: + +1) Upgrade your system to a supported FreeBSD stable or release / security +branch (releng) dated after the correction date. Restart all the affected +applications and daemons, or reboot the system. + +2) To update your system via a binary patch: + +Systems running a RELEASE version of FreeBSD on the i386 or amd64 +platforms can be updated via the freebsd-update(8) utility: + +# freebsd-update fetch +# freebsd-update install + +Restart all the affected applications and daemons, or reboot the system. + +3) To update your system via a source code patch: + +The following patches have been verified to apply to the applicable +FreeBSD release branches. + +a) Download the relevant patch from the location below, and verify the +detached PGP signature using your PGP utility. + +# fetch https://security.FreeBSD.org/patches/EN-19:08/tzdata-2019a.patch +# fetch https://security.FreeBSD.org/patches/EN-19:08/tzdata-2019a.patch.asc +# gpg --verify tzdata-2019a.patch.asc + +b) Apply the patch. Execute the following commands as root: + +# cd /usr/src +# patch < /path/to/patch + +c) Recompile the operating system using buildworld and installworld as +described in . + +Restart all the affected applications and daemons, or reboot the system. + +VI. Correction details + +The following list contains the correction revision numbers for each +affected branch. + +Branch/path Revision +- ------------------------------------------------------------------------- +stable/12/ r345669 +releng/12.0/ r347584 +stable/11/ r345670 +releng/11.2/ r347584 +- ------------------------------------------------------------------------- + +To see which files were modified by a particular revision, run the +following command, replacing NNNNNN with the revision number, on a +machine with Subversion installed: + +# svn diff -cNNNNNN --summarize svn://svn.freebsd.org/base + +Or visit the following URL, replacing NNNNNN with the revision number: + + + +VII. References + +The latest revision of this advisory is available at + +-----BEGIN PGP SIGNATURE----- + +iQKTBAEBCgB9FiEE/A6HiuWv54gCjWNV05eS9J6n5cIFAlzbTplfFIAAAAAALgAo +aXNzdWVyLWZwckBub3RhdGlvbnMub3BlbnBncC5maWZ0aGhvcnNlbWFuLm5ldEZD +MEU4NzhBRTVBRkU3ODgwMjhENjM1NUQzOTc5MkY0OUVBN0U1QzIACgkQ05eS9J6n +5cK4Dw//Y28mXrmzitCE3RclEPrP90hcRYOzknKv8xLYNo3SnCOfSnJCQqoeNw/X +HoAgX5Blm1sSYJ7GvK+AmKVn6FLoRGyd2tLzK5lofpbuExqrIZM6crHUx7HrblfO +4EfUJsIPr70y0+DeD4lBgZtpV5umOVFVWz8plgyeffGwTG3qNEES8RLI62uMrtpW +bkp+/l90eo2P9Wo34DqZSwW4V7JUwmFqooF4akZ0NBJnGpyz0iK+EZjluiRnsZxT +ueG5yqh5BpPPQ4UTxkTMoFrF2cKP18cDzQ2e1Z27JF+MpfW3Ki4zBLcmbFrVdHhR +1vlw1uIVKzusntEYX05oJUG8nkXckf6b7Wr6i1hD8tC7xgg4uBvTU4k/nLuGOHE/ +Oe6pAfLHvFS2ISk97FtImJd3UHR62+ZVX544dOxnY8N86tTU8p9vaO2AnfvTxzMR +5lyqIHgDd1RWH41aASin2fM3jeXUTubq5UsTiujaFUM5Cqoe8u5UrDAzFjxx8y2H +Uci9zi0IggRp7z8HbiXLtmoqqzwuUkXIk36j2CT7JLwH/QiP2w34Euh2wrWAeblG +tpITlvvMl9B1+zljUCxs1+8++Q/jLbhmsH1U+r7Qj6CKAg/9hCmNYZp5WAmwDHfY +V1JMNu6eaZpbCscJu9/QTsnvWiZZFBdHFubUueFsBNoKyQGVDkw= +=69LY +-----END PGP SIGNATURE----- diff --git a/share/security/advisories/FreeBSD-EN-19:09.xinstall.asc b/share/security/advisories/FreeBSD-EN-19:09.xinstall.asc new file mode 100644 index 0000000000..49d63b9921 --- /dev/null +++ b/share/security/advisories/FreeBSD-EN-19:09.xinstall.asc @@ -0,0 +1,128 @@ +-----BEGIN PGP SIGNED MESSAGE----- +Hash: SHA512 + +============================================================================= +FreeBSD-EN-19:09.xinstall Errata Notice + The FreeBSD Project + +Topic: install(1) broken with partially matching relative paths + +Category: core +Module: xinstall +Announced: 2019-05-14 +Affects: All supported versions of FreeBSD +Corrected: 2019-02-16 04:48:30 UTC (stable/12, 12.0-STABLE) + 2019-05-14 22:51:49 UTC (releng/12.0, 12.0-RELEASE-p4) + 2019-02-16 04:49:10 UTC (stable/11, 11.3-PRERELEASE) + 2019-05-14 22:51:49 UTC (releng/11.2, 11.2-RELEASE-p10) + +For general information regarding FreeBSD Errata Notices and Security +Advisories, including descriptions of the fields above, security +branches, and the following sections, please visit +. + +I. Background + +The install(1) utility installs files and links, optionally calculating +relative paths for an installed symbolic link. + +II. Problem Description + +Due to an issue in the way install(1) determines common components of the +source and target paths, the relative link may be incorrectly calculated and +drop a component of the link because a partial match existed on that +component. + +III. Impact + +The ports tree and other software very frequently use install(1) to create +relative symlinks without checking whether a partial match of the path +exists that would result in such a truncation. + +IV. Workaround + +No workaround is available, but using install(1) to install non-relative +links and files is unaffected. + +V. Solution + +Perform one of the following: + +1) Upgrade your system to a supported FreeBSD stable or release / security +branch (releng) dated after the correction date. + +2) To update your system via a binary patch: + +Systems running a RELEASE version of FreeBSD on the i386 or amd64 +platforms can be updated via the freebsd-update(8) utility: + +# freebsd-update fetch +# freebsd-update install + +3) To update your system via a source code patch: + +The following patches have been verified to apply to the applicable +FreeBSD release branches. + +a) Download the relevant patch from the location below, and verify the +detached PGP signature using your PGP utility. + +# fetch https://security.FreeBSD.org/patches/EN-19:09/xinstall.patch +# fetch https://security.FreeBSD.org/patches/EN-19:09/xinstall.patch.asc +# gpg --verify xinstall.patch.asc + +b) Apply the patch. Execute the following commands as root: + +# cd /usr/src +# patch < /path/to/patch + +c) Recompile the operating system using buildworld and installworld as +described in . + +VI. Correction details + +The following list contains the correction revision numbers for each +affected branch. + +Branch/path Revision +- ------------------------------------------------------------------------- +stable/12/ r344205 +releng/12.0/ r347585 +stable/11/ r344206 +releng/11.2/ r347585 +- ------------------------------------------------------------------------- + +To see which files were modified by a particular revision, run the +following command, replacing NNNNNN with the revision number, on a +machine with Subversion installed: + +# svn diff -cNNNNNN --summarize svn://svn.freebsd.org/base + +Or visit the following URL, replacing NNNNNN with the revision number: + + + +VII. References + + + +The latest revision of this advisory is available at + +-----BEGIN PGP SIGNATURE----- + +iQKTBAEBCgB9FiEE/A6HiuWv54gCjWNV05eS9J6n5cIFAlzbTqhfFIAAAAAALgAo +aXNzdWVyLWZwckBub3RhdGlvbnMub3BlbnBncC5maWZ0aGhvcnNlbWFuLm5ldEZD +MEU4NzhBRTVBRkU3ODgwMjhENjM1NUQzOTc5MkY0OUVBN0U1QzIACgkQ05eS9J6n +5cJV2RAAjFslsJRGQlL5piJPcAixaQO3gEgmaAp+q79whcsN3O8cqQpApU0BApTA +cT7cNnm3/sMteHFd6wCTLsssBnDsTWYxqccOeUIiCIgpXXkP67XYpLxxjBZqq5Tn +egFesjpZdu2yr+0gdRrpf54msed7ts8E0dDVoGIYeGhU7omIqlYWJGJfsZ4tg1La +Mod40JgxXcHMTca7Et46LBu/j/cF5MeQhzIepRrj1awiElQY/dMesmJwD9AuYL9m +cuS7yTH4eC6A/b7TdhUXBqBTbNipUCmwUuIWJ6OxpcrKPrtv/qGhUCEDdsNvMxpA +i8ciQY4YD06wdmZP+9Ugp/qXMXpLlxzwHrUYPe/Xn6/NvUgMp+KyMWgfkmtPBuIl +YKRTp5S4ZAs6U7RPSOMUWmQ2bWh0yZqEaQXAgzzNwIpqdghrZj73krr99pCeWc81 +1MWv6K9/ZMdm8i31Iur3Mz/4hkv5WQSObU9SdjigtvFGu5ldVEJzE5f3Zu9Vr5ja +keCB1HVYtU25ekngLYPdFiVf9B/HAWwHugOyeZNV2jPB6VVSeFkyeicm8zZ95G63 +Ww0BQbc830AFYlhb6DpciaP1Epokywr+wO4O+I3DRN3K6Zi47ODv7881milM8KQO +jWYn0kemMIgnz0R0ZluU/I5SU1cnXbWZuKvsw9efd++irqEHrBw= +=t05i +-----END PGP SIGNATURE----- diff --git a/share/security/advisories/FreeBSD-EN-19:10.scp.asc b/share/security/advisories/FreeBSD-EN-19:10.scp.asc new file mode 100644 index 0000000000..bd1e18dd6f --- /dev/null +++ b/share/security/advisories/FreeBSD-EN-19:10.scp.asc @@ -0,0 +1,125 @@ +-----BEGIN PGP SIGNED MESSAGE----- +Hash: SHA512 + +============================================================================= +FreeBSD-EN-19:10.scp Errata Notice + The FreeBSD Project + +Topic: Insufficient filename validation in scp(1) client + +Category: contrib +Module: scp +Announced: 2019-05-14 +Affects: All supported versions of FreeBSD. +Corrected: 2019-05-07 19:48:39 UTC (stable/12, 12.0-STABLE) + 2019-05-14 22:54:17 UTC (releng/12.0, 12.0-RELEASE-p10) +CVE Name: CVE-2019-6111 + +For general information regarding FreeBSD Errata Notices and Security +Advisories, including descriptions of the fields above, security +branches, and the following sections, please visit +. + +I. Background + +scp(1) is a file transfer protocol running over an SSH session. + +II. Problem Description + +The scp(1) client implementation fails to verify if the objects returned by +the server match what was requested. + +III. Impact + +A malicious scp server can write arbitrary files to the client. + +IV. Workaround + +Switch to using the sftp(1) client, if possible. + +V. Solution + +Note: While stable/11 and its release branches are currently affected by this +errata, due to the lack of patches, no fix is currently available for +stable/11. We are currently evaluating a backport for these fixes to +stable/11. + +Perform one of the following: + +1) Upgrade your system to a supported FreeBSD stable or release / security +branch (releng) dated after the correction date. + +2) To update your system via a binary patch: + +Systems running a RELEASE version of FreeBSD on the i386 or amd64 +platforms can be updated via the freebsd-update(8) utility: + +# freebsd-update fetch +# freebsd-update install + +3) To update your system via a source code patch: + +The following patches have been verified to apply to the applicable +FreeBSD release branches. + +a) Download the relevant patch from the location below, and verify the +detached PGP signature using your PGP utility. + +[FreeBSD 12.0] +# fetch https://security.FreeBSD.org/patches/EN-19:10/scp.patch +# fetch https://security.FreeBSD.org/patches/EN-19:10/scp.patch.asc +# gpg --verify scp.patch.asc + +b) Apply the patch. Execute the following commands as root: + +# cd /usr/src +# patch < /path/to/patch + +c) Recompile the operating system using buildworld and installworld as +described in . + +VI. Correction details + +The following list contains the correction revision numbers for each +affected branch. + +Branch/path Revision +- ------------------------------------------------------------------------- +stable/12/ r347232 +releng/12.0/ r347586 +- ------------------------------------------------------------------------- + +To see which files were modified by a particular revision, run the +following command, replacing NNNNNN with the revision number, on a +machine with Subversion installed: + +# svn diff -cNNNNNN --summarize svn://svn.freebsd.org/base + +Or visit the following URL, replacing NNNNNN with the revision number: + + + +VII. References + + + +The latest revision of this advisory is available at + +-----BEGIN PGP SIGNATURE----- + +iQKTBAEBCgB9FiEE/A6HiuWv54gCjWNV05eS9J6n5cIFAlzbTq1fFIAAAAAALgAo +aXNzdWVyLWZwckBub3RhdGlvbnMub3BlbnBncC5maWZ0aGhvcnNlbWFuLm5ldEZD +MEU4NzhBRTVBRkU3ODgwMjhENjM1NUQzOTc5MkY0OUVBN0U1QzIACgkQ05eS9J6n +5cJXGQ/+Ii19QUq6MdSeNPPOHVTtW8G/FIlsaYYlCFooIvzxYxvcqDcCyabVlX/a +Lt815YY7+EbKcSbA0Gh/YFm9S05rwUg4Dnj8nIQwMVp9OEtziIdY6TVU0JhRoUpe ++YVG9e5eh8wK7FFJ/jIaZbAcr2MfMYV2KPouA1HZdqsMBkAkr8xuS3HrmkeE0nxo +6QHTWaaD7qvr8foUSHS1hJsAX3+1eIsdytGUTJIGeL6g7DWsLYYiX7v2k+eZuSe1 +dkt7/3J+RqpyJAv+LfGh3QnILC52fO7jOVlnOBt5H/HefX+xRdb8lwHfoBeyxIFc +N4v4Ecypewci6Hv4moTeZF+FtIETHj3EfPIe04eiikiGhrpGQ4cCveK6+kk49x4m +RR7TE+y7klGIfoSuxoooaJ1/UyFJ9T0eICmBUh1B5rcrnwbbhgpXVPpbbee7IFL2 +HYiEuDECPN45zek+bL0M5D0wHZc823e7p1Ioxl1NNzawdts7hWwIpNmFTlfWNczQ +KZ9y0bDFffK3nuUkMHORLagCM6ou/wAPunsnWXY3Xg3X61svYIvZThDIeeOi9SbF +d1ve8/H/t5yHRQBpqWk51FfO4RdPmQAo6Y9w9WzhnkETsNXeTruQq7D8SnOaWgXG +JUh9PAVQKcJRWPXVwDTPEsqRgaDVB0gpaPCt5IS2j2tyB8UuAd4= +=2h+W +-----END PGP SIGNATURE----- diff --git a/share/security/advisories/FreeBSD-SA-19:03.wpa.asc b/share/security/advisories/FreeBSD-SA-19:03.wpa.asc new file mode 100644 index 0000000000..219ebfd1d9 --- /dev/null +++ b/share/security/advisories/FreeBSD-SA-19:03.wpa.asc @@ -0,0 +1,154 @@ +-----BEGIN PGP SIGNED MESSAGE----- +Hash: SHA512 + +============================================================================= +FreeBSD-SA-19:03.wpa Security Advisory + The FreeBSD Project + +Topic: Multiple vulnerabilities in hostapd and wpa_supplicant + +Category: contrib +Module: wpa +Announced: 2019-05-14 +Affects: All supported versions of FreeBSD. +Corrected: 2019-05-01 01:42:38 UTC (stable/12, 12.0-STABLE) + 2019-05-14 22:57:29 UTC (releng/12.0, 12.0-RELEASE-p4) + 2019-05-01 01:43:17 UTC (stable/11, 11.2-STABLE) + 2019-05-14 22:59:32 UTC (releng/11.2, 11.2-RELEASE-p10) +CVE Name: CVE-2019-9494, CVE-2019-9495, CVE-2019-9496, CVE-2019-9497, + CVE-2019-9498, CVE-2019-9499, CVE-2019-11555 + +For general information regarding FreeBSD Security Advisories, +including descriptions of the fields above, security branches, and the +following sections, please visit . + +I. Background + +Wi-Fi Protected Access II (WPA2) is a security protocol developed by the +Wi-Fi Alliance to secure wireless computer networks. + +hostapd(8) and wpa_supplicant(8) are implementations of user space daemon for +access points and wireless client that implements the WPA2 protocol. + +II. Problem Description + +Multiple vulnerabilities exist in the hostapd(8) and wpa_supplicant(8) +implementations. For more details, please see the reference URLs in the +References section below. + +III. Impact + +Security of the wireless network may be compromised. For more details, +please see the reference URLS in the References section below. + +IV. Workaround + +No workaround is available, but systems not using hostapd(8) or +wpa_supplicant(8) are not affected. + +V. Solution + +Perform one of the following: + +1) Upgrade your vulnerable system to a supported FreeBSD stable or +release / security branch (releng) dated after the correction date. + +Afterwards, restart hostapd(8) or wpa_supplicant(8). + +2) To update your vulnerable system via a binary patch: + +Systems running a RELEASE version of FreeBSD on the i386 or amd64 +platforms can be updated via the freebsd-update(8) utility: + +# freebsd-update fetch +# freebsd-update install + +Afterwards, restart hostapd(8) or wpa_supplicant(8). + +3) To update your vulnerable system via a source code patch: + +The following patches have been verified to apply to the applicable +FreeBSD release branches. + +a) Download the relevant patch from the location below, and verify the +detached PGP signature using your PGP utility. + +[FreeBSD 12.0] +# fetch https://security.FreeBSD.org/patches/SA-19:03/wpa-12.patch +# fetch https://security.FreeBSD.org/patches/SA-19:03/wpa-12.patch.asc +# gpg --verify wpa-12.patch.asc + +[FreeBSD 11.2] +# fetch https://security.FreeBSD.org/patches/SA-19:03/wpa-11.patch +# fetch https://security.FreeBSD.org/patches/SA-19:03/wpa-11.patch.asc +# gpg --verify wpa-11.patch.asc + +b) Apply the patch. Execute the following commands as root: + +# cd /usr/src +# patch < /path/to/patch + +c) Recompile the operating system using buildworld and installworld as +described in . + +Restart the applicable daemons, or reboot the system. + +VI. Correction details + +The following list contains the correction revision numbers for each +affected branch. + +Branch/path Revision +- ------------------------------------------------------------------------- +stable/12/ r346980 +releng/12.0/ r347587 +stable/11/ r346981 +releng/11.2/ r347588 +- ------------------------------------------------------------------------- + +To see which files were modified by a particular revision, run the +following command, replacing NNNNNN with the revision number, on a +machine with Subversion installed: + +# svn diff -cNNNNNN --summarize svn://svn.freebsd.org/base + +Or visit the following URL, replacing NNNNNN with the revision number: + + + +VII. References + + + + + + + + + + + + + + + +The latest revision of this advisory is available at + +-----BEGIN PGP SIGNATURE----- + +iQKTBAEBCgB9FiEE/A6HiuWv54gCjWNV05eS9J6n5cIFAlzbTrVfFIAAAAAALgAo +aXNzdWVyLWZwckBub3RhdGlvbnMub3BlbnBncC5maWZ0aGhvcnNlbWFuLm5ldEZD +MEU4NzhBRTVBRkU3ODgwMjhENjM1NUQzOTc5MkY0OUVBN0U1QzIACgkQ05eS9J6n +5cLsaA/9EB577JYdYdwFCOQ6TiOVhyluLJzgrhG3aiXeBntj8ytkRjcXKnP0aega +3G2R1do7pixVYUF52OWJwaNO3Hm+LHMngiOqujcLI+49ISI3T/APaU/D2dqmXVb8 +nN/Pd+0HDGj3R3MwyyHT8/3fX0pJ395vcQhYb61M6PUSrwr8uiBbILT57iCadZoL +F4KOCvRv7I4EFWXvqngGfeohZbbeHPBga2DwuebWR/E/1uWrMKEOF2pvh4b6ZSN2 +pdr7ZHMiL1cZt+p+2gwWoqDWyD93u2lTC7Gmo3Vom+meH7eaQ79obXEN541aiQ04 +CYhjkwuW5uNGUWCO/Xsfn5gqICeB1G5A/aBHQlAyVgUGia8jukL1jn3ga4AQgKrN +h9aTmvrQs17PjMVtq81ZS0xm0ztW0Y6t2A9fRgGcnOOw+uy5tHMbJaKSMy8x97NT +gUyXtoyu47tjjMrzsQcma2t6/+iCEDuW1P1LybSmv/v59gro9uveCdl0busgM9GS +M5bpWK/qYQS1HYmYeTKMRynmD8ntRbflYoUP/SpijHsz+56rgyeJO12WyltyT32f +j5fgnKaznW/UPtgmK0wnPIG9XEj3Nzs4C4cypO5t8OiuLEli4wRdb6MYlvEjq4la +R3lnCzmTd9sg+K6cod2qWWSYdsdEwizcpQDp7M9lRqomiANLqJ4= +=MXma +-----END PGP SIGNATURE----- diff --git a/share/security/advisories/FreeBSD-SA-19:04.ntp.asc b/share/security/advisories/FreeBSD-SA-19:04.ntp.asc new file mode 100644 index 0000000000..de2dd01682 --- /dev/null +++ b/share/security/advisories/FreeBSD-SA-19:04.ntp.asc @@ -0,0 +1,146 @@ +-----BEGIN PGP SIGNED MESSAGE----- +Hash: SHA512 + +============================================================================= +FreeBSD-SA-19:04.ntp Security Advisory + The FreeBSD Project + +Topic: Authenticated denial of service in ntpd + +Category: contrib +Module: ntp +Announced: 2019-05-14 +Credits: Magnus Stubman +Affects: All supported versions of FreeBSD +Corrected: 2019-03-07 13:45:36 UTC (stable/12, 12.0-STABLE) + 2019-05-14 23:02:56 UTC (releng/12.0, 12.0-RELEASE-p4) + 2019-03-07 13:45:36 UTC (stable/11, 11.3-PRERELEASE) + 2019-05-14 23:06:26 UTC (releng/11.2, 11.2-RELEASE-p10) +CVE Name: CVE-2019-8936 + +For general information regarding FreeBSD Security Advisories, +including descriptions of the fields above, security branches, and the +following sections, please visit . + +I. Background + +The ntpd(8) daemon is an implementation of the Network Time Protocol +(NTP) used to synchronize the time of a computer system to a reference +time source. The ntpd(8) daemon uses a protocol called mode 6 to both get +status information from the running ntpd(8) daemon and configure it on the +fly. This protocol is typically used by the ntpq(8) program, among others. + +II. Problem Description + +A crafted malicious authenticated mode 6 packet from a permitted network +address can trigger a NULL pointer dereference. + +Note for this attack to work, the sending system must be on an address from +which the target ntpd(8) accepts mode 6 packets, and must use a private key +that is specifically listed as being used for mode 6 authorization. + +III. Impact + +The ntpd daemon can crash due to the NULL pointer dereference, causing a +denial of service. + +IV. Workaround + +Use 'restrict noquery' in the ntpd configuration to limit addresses that +can send mode 6 queries. + +V. Solution + +Perform one of the following: + +1) Upgrade your vulnerable system to a supported FreeBSD stable or +release / security branch (releng) dated after the correction date. + +2) To update your vulnerable system via a binary patch: + +Systems running a RELEASE version of FreeBSD on the i386 or amd64 +platforms can be updated via the freebsd-update(8) utility: + +# freebsd-update fetch +# freebsd-update install + +Afterwards, restart the ntpd service: +# service ntpd restart + +3) To update your vulnerable system via a source code patch: + +The following patches have been verified to apply to the applicable +FreeBSD release branches. + +a) Download the relevant patch from the location below, and verify the +detached PGP signature using your PGP utility. + +[FreeBSD 12.0] +# fetch https://security.FreeBSD.org/patches/SA-19:04/ntp.patch +# fetch https://security.FreeBSD.org/patches/SA-19:04/ntp.patch.asc +# gpg --verify ntp.patch.asc + +[FreeBSD 11.2-RELEASE/11.3-PRERELEASE] +# fetch https://security.FreeBSD.org/patches/SA-19:04/ntp-11.2.patch +# fetch https://security.FreeBSD.org/patches/SA-19:04/ntp-11.2.patch.asc +# gpg --verify ntp-11.2.patch.asc + +b) Apply the patch. Execute the following commands as root: + +# cd /usr/src +# patch < /path/to/patch + +c) Recompile the operating system using buildworld and installworld as +described in . + +Restart the ntpd service, or reboot the system. + +VI. Correction details + +The following list contains the correction revision numbers for each +affected branch. + +Branch/path Revision +- ------------------------------------------------------------------------- +stable/12/ r344884 +releng/12.0/ r347589 +stable/11/ r344884 +releng/11.2/ r347590 +- ------------------------------------------------------------------------- + +To see which files were modified by a particular revision, run the +following command, replacing NNNNNN with the revision number, on a +machine with Subversion installed: + +# svn diff -cNNNNNN --summarize svn://svn.freebsd.org/base + +Or visit the following URL, replacing NNNNNN with the revision number: + + + +VII. References + + + + + +The latest revision of this advisory is available at + +-----BEGIN PGP SIGNATURE----- + +iQKTBAEBCgB9FiEE/A6HiuWv54gCjWNV05eS9J6n5cIFAlzbTrdfFIAAAAAALgAo +aXNzdWVyLWZwckBub3RhdGlvbnMub3BlbnBncC5maWZ0aGhvcnNlbWFuLm5ldEZD +MEU4NzhBRTVBRkU3ODgwMjhENjM1NUQzOTc5MkY0OUVBN0U1QzIACgkQ05eS9J6n +5cLGtw/8CNAYnLxARrMUK1QeC9sE7EaboYInSOgaunfK2Uw5tJk9b4GwWWjCSE0C +hSWg4a9xv3pks2ppfEJzRuy0eoYmiU0MYblnAnCwCmE2d3WYlExO7hZJa1iK3uPO +WvHre5q80kF8TJhS9rbph+6oyLaPun8f9PDIo4Oc2knTppNfrfzbB/HEuzP27KMp +gCXD/Nk/5tHbXjkIGamWCf9wgYuw/typYRV3W6sWDuPhug2sAvWk1TMo0cMJ4BHL +wL7Qh00rZ+nHWdk5GKFslga9gNjVPqD2DzRKCQO2bj4o+7ly2d+yk4jUpMKBq2r4 +eQcQQnk9xj60NQ5cHGprOv6xwulBYycugF57iouNAP241cvVf+XZd4b/GthJODgz +fhP0aquusmtkawida3ZWWIVCjkM5NmHQsY5VTQLvTudtemb3kdmRMy3dFDN7oyXZ +PqP6JJUqamxNHilxRVytNCZLiSuy1P2MnJamyLZIqcDiT6yvMVBqwuGdQrSTSKyu +g/sR+vUohuJrP2i3pCCEfGtH5Nfq6GpY6Swxec81wUoqReGVCGmSFSEaas21TFYf +ZzAEAhywveGegkhqvsGP9A1zrTs6ZTCRzun32MhSo4xH/YZaArMvRa6JiSWTA1fG +ctwXEwIBj0XNEWBsCPgVvaF9bglmQZ2Iqn4iOiHlRGT7KxgjT7w= +=o9t5 +-----END PGP SIGNATURE----- diff --git a/share/security/advisories/FreeBSD-SA-19:05.pf.asc b/share/security/advisories/FreeBSD-SA-19:05.pf.asc new file mode 100644 index 0000000000..90a1fe7706 --- /dev/null +++ b/share/security/advisories/FreeBSD-SA-19:05.pf.asc @@ -0,0 +1,134 @@ +-----BEGIN PGP SIGNED MESSAGE----- +Hash: SHA512 + +============================================================================= +FreeBSD-SA-19:05.pf Security Advisory + The FreeBSD Project + +Topic: IPv6 fragment reassembly panic in pf(4) + +Category: contrib +Module: pf +Announced: 2019-05-14 +Credits: Synacktiv +Affects: All supported versions of FreeBSD +Corrected: 2019-03-01 18:12:05 UTC (stable/12, 12.0-STABLE) + 2019-05-14 23:10:21 UTC (releng/12.0, 12.0-RELEASE-p4) + 2019-03-01 18:12:07 UTC (stable/11, 11.3-PRERELEASE) + 2019-05-14 23:10:21 UTC (releng/11.2, 11.2-RELEASE-p10) +CVE Name: CVE-2019-5597 + +For general information regarding FreeBSD Security Advisories, +including descriptions of the fields above, security branches, and the +following sections, please visit . + +I. Background + +pf(4) is an Internet Protocol packet filter originally written for OpenBSD. +In addition to filtering packets, it also has packet normalization +capabilities. + +II. Problem Description + +A bug in the pf(4) IPv6 fragment reassembly logic incorrectly uses the last +extension header offset from the last received packet instead of from the +first packet. + +III. Impact + +Malicious IPv6 packets with different IPv6 extensions could cause a kernel +panic or potentially a filtering rule bypass. + +IV. Workaround + +Only systems leveraging the pf(4) firewall and include packet scrubbing using +the recommended 'scrub all in' or similar are affected. + +V. Solution + +Perform one of the following: + +1) Upgrade your vulnerable system to a supported FreeBSD stable or +release / security branch (releng) dated after the correction date. +Afterwards, reboot the system. + +2) To update your vulnerable system via a binary patch: + +Systems running a RELEASE version of FreeBSD on the i386 or amd64 +platforms can be updated via the freebsd-update(8) utility: + +# freebsd-update fetch +# freebsd-update install + +Afterwards, reboot the system. + +3) To update your vulnerable system via a source code patch: + +The following patches have been verified to apply to the applicable +FreeBSD release branches. + +a) Download the relevant patch from the location below, and verify the +detached PGP signature using your PGP utility. + +# fetch https://security.FreeBSD.org/patches/SA-19:05/pf.patch +# fetch https://security.FreeBSD.org/patches/SA-19:05/pf.patch.asc +# gpg --verify pf.patch.asc + +b) Apply the patch. Execute the following commands as root: + +# cd /usr/src +# patch < /path/to/patch + +c) Recompile your kernel as described in + and reboot the +system. + +VI. Correction details + +The following list contains the correction revision numbers for each +affected branch. + +Branch/path Revision +- ------------------------------------------------------------------------- +stable/12/ r344706 +releng/12.0/ r347591 +stable/11/ r344707 +releng/11.2/ r347591 +- ------------------------------------------------------------------------- + +To see which files were modified by a particular revision, run the +following command, replacing NNNNNN with the revision number, on a +machine with Subversion installed: + +# svn diff -cNNNNNN --summarize svn://svn.freebsd.org/base + +Or visit the following URL, replacing NNNNNN with the revision number: + + + +VII. References + + + + + +The latest revision of this advisory is available at + +-----BEGIN PGP SIGNATURE----- + +iQKTBAEBCgB9FiEE/A6HiuWv54gCjWNV05eS9J6n5cIFAlzbTsNfFIAAAAAALgAo +aXNzdWVyLWZwckBub3RhdGlvbnMub3BlbnBncC5maWZ0aGhvcnNlbWFuLm5ldEZD +MEU4NzhBRTVBRkU3ODgwMjhENjM1NUQzOTc5MkY0OUVBN0U1QzIACgkQ05eS9J6n +5cL1cxAAjYy90WBfuBkU/FddQWMJkXOn2YqABFxY/BfFpJEbGrnXXuxz9YJByK3b +6ikWq5HcxgL/9ek6QULwEOoNvms8tT4m4waJOLa3hZPoPlgD2ArgvdcEI00R/8T9 +Z+k1YlT0oLOY4XbVynPGNmiFNTAcsg7Ognp9yam3kmPZTMGYm6cKIBy1idrzCCmI +nj0SscyoL4Z09kSWe3UOitjh8cpxqGuvGosCb7YGPl6yTSalBUgP44Lyg7jS4nrZ +xjZxqhAfp7tk9peF4rov8apZIsrBF5GMaahnIGIwZzmRn/E1pND9qx1lB1Uh7rfR +nb8OmwbshJTWdnS1GXyLxRGJOd0zmh+YZ10ygZAQTM5sNaxfn6pWJFmr2S/mR+kN +RG/Bhj+lN7jh1eUNdwk/pAm0aZZ+J8GX4/QOrqPfGDko/s/S7YwJB/DKR/14uPY7 +Fwcgv4tvgoRstSKHdIe45d7/N0SgQCS/EfzVIO5XPQtkrk9/zalQubionijObr1Q +ARVl7H5M7m7kP8PJz/vRNvhar0c0xTk9ov2JDxKHKTd+7D78LQEAFvEGPIFREBsY +VBW8BqZbuVcsgrhr/YWFE3TEw4O0YbnY5g9wmVv+d/pdDngLuTsfbNEsAQewWcu/ +dYefeBMKBukyLUKtLYHjVAhUlL3hF3j/aBu498F6LRCzFcaoIOQ= +=0alQ +-----END PGP SIGNATURE----- diff --git a/share/security/advisories/FreeBSD-SA-19:06.pf.asc b/share/security/advisories/FreeBSD-SA-19:06.pf.asc new file mode 100644 index 0000000000..e137475380 --- /dev/null +++ b/share/security/advisories/FreeBSD-SA-19:06.pf.asc @@ -0,0 +1,134 @@ +-----BEGIN PGP SIGNED MESSAGE----- +Hash: SHA512 + +============================================================================= +FreeBSD-SA-19:06.pf Security Advisory + The FreeBSD Project + +Topic: ICMP/ICMP6 packet filter bypass in pf + +Category: contrib +Module: pf +Announced: 2019-05-14 +Credits: Synacktiv +Affects: All supported versions of FreeBSD +Corrected: 2019-03-21 14:17:10 UTC (stable/12, 12.0-STABLE) + 2019-05-14 23:12:22 UTC (releng/12.0, 12.0-RELEASE-p4) + 2019-03-21 14:17:12 UTC (stable/11, 11.3-PRERELEASE) + 2019-05-14 23:12:22 UTC (releng/11.2, 11.2-RELEASE-p10) +CVE Name: CVE-2019-5598 + +For general information regarding FreeBSD Security Advisories, +including descriptions of the fields above, security branches, and the +following sections, please visit . + +I. Background + +pf(4) is an Internet Protocol packet filter originally written for OpenBSD. +In addition to filtering packets, it also has packet normalization +capabilities. + +II. Problem Description + +States in pf(4) let ICMP and ICMP6 packets pass if they have a packet in +their payload matching an existing condition. pf(4) does not check if the +outer ICMP or ICMP6 packet has the same destination IP as the source IP of +the inner protocol packet. + +III. Impact + +A maliciously crafted ICMP/ICMP6 packet could bypass the packet filter rules +and be passed to a host that would otherwise be unavailable. + +IV. Workaround + +No workaround is available. + +V. Solution + +Perform one of the following: + +1) Upgrade your vulnerable system to a supported FreeBSD stable or +release / security branch (releng) dated after the correction date. +Afterwards, reboot the system. + +2) To update your vulnerable system via a binary patch: + +Systems running a RELEASE version of FreeBSD on the i386 or amd64 +platforms can be updated via the freebsd-update(8) utility: + +# freebsd-update fetch +# freebsd-update install + +Afterwards, reboot the system. + +3) To update your vulnerable system via a source code patch: + +The following patches have been verified to apply to the applicable +FreeBSD release branches. + +a) Download the relevant patch from the location below, and verify the +detached PGP signature using your PGP utility. + +# fetch https://security.FreeBSD.org/patches/SA-19:06/pf.patch +# fetch https://security.FreeBSD.org/patches/SA-19:06/pf.patch.asc +# gpg --verify pf.patch.asc + +b) Apply the patch. Execute the following commands as root: + +# cd /usr/src +# patch < /path/to/patch + +c) Recompile your kernel as described in + and reboot the +system. + +VI. Correction details + +The following list contains the correction revision numbers for each +affected branch. + +Branch/path Revision +- ------------------------------------------------------------------------- +stable/12/ r345377 +releng/12.0/ r347593 +stable/11/ r345378 +releng/11.2/ r347593 +- ------------------------------------------------------------------------- + +To see which files were modified by a particular revision, run the +following command, replacing NNNNNN with the revision number, on a +machine with Subversion installed: + +# svn diff -cNNNNNN --summarize svn://svn.freebsd.org/base + +Or visit the following URL, replacing NNNNNN with the revision number: + + + +VII. References + + + + + +The latest revision of this advisory is available at + +-----BEGIN PGP SIGNATURE----- + +iQKTBAEBCgB9FiEE/A6HiuWv54gCjWNV05eS9J6n5cIFAlzbTsdfFIAAAAAALgAo +aXNzdWVyLWZwckBub3RhdGlvbnMub3BlbnBncC5maWZ0aGhvcnNlbWFuLm5ldEZD +MEU4NzhBRTVBRkU3ODgwMjhENjM1NUQzOTc5MkY0OUVBN0U1QzIACgkQ05eS9J6n +5cIjXA/9FevC+Ygihzb0J9MN0znEM883dk5sPCSvMwiivsNRkDMXreYqPXU+Fkt0 +iV1OZ8tKwKAihm+iGJ5mzS5l40wWF1oDcqJrC0myICdvreraoJKZvTLhgGIBqKkE +b8yIuzPueWdnnudoAzTV38RhyaP2aOb44OMUNPQZsEB/6hHsNvp9m6yAua/F+x9+ +N9J38Y/C6udsNfhqDeuCI4G8yiN33XfFiRbF+31rt3s0rUm6KGNsJanJe8dNAEvE +DN4tA4+MORnQ7QTLgOobGuLFhWJ2urC6psH8duO72hcSTzSkTZpxrC3f6SW8RlZ+ +Pbr4LZ6FA3bZp/sCmWPOot94hotBDr03MZwrxURokeDHZU1nUBsw0rmTG4aypujl +JrGPOAp89TtqrR0zV8DhpGO/RWoBeMDf7ZGvIplOIEF5rijQWEyC5pnYlBKPfSdm +UTxcN9RoJCfz7O4KLAAqhHiuu6xc+CqlQH1dvyLbqGVv9LzUQlziTNsbQ4cGryuj +g1TztU0VfpvHDkAKBh0iHwkoUqDSut3K19rFAQ3zkM/EodqSTkE1OG77pmsjYaVq +AfcnN/se8lklq0lKi3BwNvVIWTjhMAwY63otVxvVD4wrJrgQH8NKgOeYuGBreXeW +Uv569bIhR0/vsyGJK/SMKxBiAGfzkE7LqDMJqdXLsompX97nOwI= +=m3as +-----END PGP SIGNATURE----- diff --git a/share/security/advisories/FreeBSD-SA-19:07.mds.asc b/share/security/advisories/FreeBSD-SA-19:07.mds.asc new file mode 100644 index 0000000000..42ca84a7c8 --- /dev/null +++ b/share/security/advisories/FreeBSD-SA-19:07.mds.asc @@ -0,0 +1,198 @@ +-----BEGIN PGP SIGNED MESSAGE----- +Hash: SHA512 + +============================================================================= +FreeBSD-SA-19:07.mds Security Advisory + The FreeBSD Project + +Topic: Microarchitectural Data Sampling (MDS) + +Category: core +Module: kernel +Announced: 2019-05-14 +Credits: Refer to Intel's security advisory at the URL below for + detailed acknowledgements. +Affects: All supported versions of FreeBSD. +Corrected: 2019-05-14 17:04:00 UTC (stable/12, 12.0-STABLE) + 2019-05-14 23:19:08 UTC (releng/12.0, 12.0-RELEASE-p4) + 2019-05-14 17:05:02 UTC (stable/11, 11.3-PRERELEASE) + 2019-05-14 23:20:16 UTC (releng/11.2, 11.2-RELEASE-p10) +CVE Name: CVE-2018-12126, CVE-2018-12127, CVE-2018-12130, + CVE-2019-11091 + +For general information regarding FreeBSD Security Advisories, +including descriptions of the fields above, security branches, and the +following sections, please visit . + +I. Background + +Modern processors make use of speculative execution, an optimization +technique which performs some action in advance of knowing whether the +result will actually be used. + +II. Problem Description + +On some Intel processors utilizing speculative execution a local process may +be able to infer stale information from microarchitectural buffers to obtain +a memory disclosure. + +III. Impact + +An attacker may be able to read secret data from the kernel or from a +process when executing untrusted code (for example, in a web browser). + +IV. Workaround + +No workaround is available. + +Systems with users or processors in different trust domains should disable +Hyper-Threading by setting the machdep.hyperthreading_allowed tunable to 0: + +# echo 'machdep.hyperthreading_allowed=0 >> /boot/loader.conf' +# shutdown + +V. Solution + +Perform one of the following: + +Update CPU microcode, upgrade your vulnerable system to a supported FreeBSD +stable or release / security branch (releng) dated after the correction date, +evaluate mitigation and Hyper Threading controls, and reboot the system. + +New CPU microcode may be available in a BIOS update from your system vendor, +or by installing the devcpu-data package or sysutils/devcpu-data port. +Ensure that the BIOS update or devcpu-data package is dated after 2014-05-14. + +If using the package or port the microcode update can be applied at boot time +by adding the following lines to the system's /boot/loader.conf: + +cpu_microcode_load="YES" +cpu_microcode_name="/boot/firmware/intel-ucode.bin" + +Microcode updates can also be applied while the system is running. See +cpucontrol(8) for details. + +1) To update your vulnerable system via a binary patch: + +Systems running a RELEASE version of FreeBSD on the i386 or amd64 +platforms can be updated via the freebsd-update(8) utility: + +# freebsd-update fetch +# freebsd-update install + +Follow additional details under "Mitigation Configuration" below. + +2) To update your vulnerable system via a source code patch: + +The following patches have been verified to apply to the applicable +FreeBSD release branches. + +a) Download the relevant patch from the location below, and verify the +detached PGP signature using your PGP utility. + +[FreeBSD 12.0-STABLE] +# fetch https://security.FreeBSD.org/patches/SA-19:07/mds.12-stable.patch +# fetch https://security.FreeBSD.org/patches/SA-19:07/mds.12-stable.patch.asc +# gpg --verify mds.12-stable.patch.asc + +[FreeBSD 12.0-RELEASE] +# fetch https://security.FreeBSD.org/patches/SA-19:07/mds.12.0.patch +# fetch https://security.FreeBSD.org/patches/SA-19:07/mds.12.0.patch.asc +# gpg --verify mds.12.0.patch.asc + +[FreeBSD 11.3-PRERELEASE] +# fetch https://security.FreeBSD.org/patches/SA-19:07/mds.11-stable.patch +# fetch https://security.FreeBSD.org/patches/SA-19:07/mds.11-stable.patch.asc +# gpg --verify mds.11-stable.patch.asc + +[FreeBSD 11.2-RELEASE] +# fetch https://security.FreeBSD.org/patches/SA-19:07/mds.11.2.patch +# fetch https://security.FreeBSD.org/patches/SA-19:07/mds.11.2.patch.asc +# gpg --verify mds.11.2.patch.asc + +b) Apply the patch. Execute the following commands as root: + +# cd /usr/src +# patch < /path/to/patch + +c) Recompile your kernel as described in +. + +Mitigation Configuration + +Systems with users, processes, or virtual machines in different trust +domains should disable Hyper-Threading by setting the +machdep.hyperthreading_allowed tunable to 0: + +# echo machdep.hyperthreading_allowed=0 >> /boot/loader.conf + +To activate the MDS mitigation set the hw.mds_disable sysctl. The settings +are: + +0 - mitigation disabled +1 - VERW instruction (microcode) mitigation enabled +2 - Software sequence mitigation enabled (not recommended) +3 - Automatic VERW or Software selection + +Automatic mode uses the VERW instruction if supported by the CPU / microcode, +or software sequences if not. To enable automatic mode at boot: + +# echo hw.mds_disable=3 >> /etc/sysctl.conf + +Reboot the system: + +# shutdown -r +10min "Security update" + +Check the mitigation status: + +# sysctl hw.mds_disable_state +hw.mds_disable_state: software Silvermont + +VI. Correction details + +The following list contains the correction revision numbers for each +affected branch. + +Branch/path Revision +- ------------------------------------------------------------------------- +stable/12/ r347567 +releng/12.0/ r346594 +stable/11/ r347568 +releng/11.2/ r347595 +- ------------------------------------------------------------------------- + +To see which files were modified by a particular revision, run the +following command, replacing NNNNNN with the revision number, on a +machine with Subversion installed: + +# svn diff -cNNNNNN --summarize svn://svn.freebsd.org/base + +Or visit the following URL, replacing NNNNNN with the revision number: + + + +VII. References + + + + +The latest revision of this advisory is available at + +-----BEGIN PGP SIGNATURE----- + +iQKTBAEBCgB9FiEE/A6HiuWv54gCjWNV05eS9J6n5cIFAlzbTspfFIAAAAAALgAo +aXNzdWVyLWZwckBub3RhdGlvbnMub3BlbnBncC5maWZ0aGhvcnNlbWFuLm5ldEZD +MEU4NzhBRTVBRkU3ODgwMjhENjM1NUQzOTc5MkY0OUVBN0U1QzIACgkQ05eS9J6n +5cKcyA//ZlJa5eoNt0L2pcWAjukf1X+/iTjHv/t3wWclEfuPv2S9lO5SDlwxUV5x +woGkxcIj7Tp51HJZRBjn62x/cwd6CjbpxsYPUvRs1Nkruj82/p6Yj5nSYrDCqqj1 +k84hyCj0Y6V2NwbBEPTNXqqPbOmid0R3GrQJk1JXZ1zTf8VHGxrquXp1xP7PIPSX +GWYup0k4edMCY2mbBb8QQQmQSg6S2k6eZnvF9AZUga5pM7FGYLo0rPHNVHx+te83 +THvmnrJXnCR5AEjqmsubxwF/p+HneJke7HJxj1GjokzFgzTz3C9X3vUWHedwlVoD +BzeqSgWD0icgJMYl8xGabeRzXj49tIzrC+twdXMtTLiDIKGxaRxqGVTMHYHgh44h +GilgZ60X4m8e4Nuzf8xcQ1X2/QLvfWwZR+zUzQwOiKVoNp7nPJ5m8nr1s9anqDdl +n1fJw3tqw+8ant58k71IKD5lCV0KhJXgD/Kd3TZWu9a4mnMlvuJWYbEKEvxSlvTh +ghORCSg+OBEgN//t9a/3UaAOzqKijkN6Iau1JpMrFNtBOXgOO17B1jQGz1R2VKKb +mu5gotDQqkdQocN+94sB8T3fouSa6ub2cUox34+DngqxuFeMv6Ffg1o/Z4C0mRUu +bVdzPrsUai/Z7O/kBpUF6ddsBGsDXWElfo9flfbJonLcYndWyWc= +=QUYl +-----END PGP SIGNATURE----- diff --git a/share/security/patches/EN-19:08/tzdata-2019a.patch b/share/security/patches/EN-19:08/tzdata-2019a.patch new file mode 100644 index 0000000000..50037dffec --- /dev/null +++ b/share/security/patches/EN-19:08/tzdata-2019a.patch @@ -0,0 +1,590 @@ +--- contrib/tzdata/Makefile.orig ++++ contrib/tzdata/Makefile +@@ -12,7 +12,10 @@ + # Email address for bug reports. + BUGEMAIL= tz@iana.org + +-# Choose source data features. To get new features right away, use: ++# DATAFORM selects the data format. ++# Available formats represent essentially the same data, albeit ++# possibly with minor discrepancies that users are not likely to notice. ++# To get new features and the best data right away, use: + # DATAFORM= vanguard + # To wait a while before using new features, to give downstream users + # time to upgrade zic (the default), use: +@@ -33,11 +36,11 @@ + LOCALTIME= GMT + + # If you want something other than Eastern United States time as a template +-# for handling POSIX-style timezone environment variables, ++# for handling ruleless POSIX-style timezone environment variables, + # change the line below (after finding the timezone you want in the + # one of the $(TDATA) source files, or adding it to a source file). +-# When a POSIX-style environment variable is handled, the rules in the +-# template file are used to determine "spring forward" and "fall back" days and ++# A ruleless environment setting like TZ='CST6CDT' uses the rules in the ++# template file to determine "spring forward" and "fall back" days and + # times; the environment variable itself specifies UT offsets of standard and + # daylight saving time. + # Alternatively, if you discover you've got the wrong timezone, you can just +@@ -46,7 +49,6 @@ + # Use the command + # make zonenames + # to get a list of the values you can use for POSIXRULES. +-# If you want POSIX compatibility, use "America/New_York". + + POSIXRULES= America/New_York + +@@ -113,8 +115,8 @@ + TIME_T_ALTERNATIVES_HEAD = int64_t + TIME_T_ALTERNATIVES_TAIL = int32_t uint32_t uint64_t + +-# What kind of TZif data files to generate. +-# (TZif is the binary time zone data format that zic generates.) ++# What kind of TZif data files to generate. (TZif is the binary time ++# zone data format that zic generates; see Internet RFC 8536.) + # If you want only POSIX time, with time values interpreted as + # seconds since the epoch (not counting leap seconds), use + # REDO= posix_only +@@ -360,6 +362,9 @@ + zic= ./zic + ZIC= $(zic) $(ZFLAGS) + ++# To shrink the size of installed TZif files, ++# append "-r @N" to omit data before N-seconds-after-the-Epoch. ++# See the zic man page for more about -r. + ZFLAGS= + + # How to use zic to install TZif files. +@@ -491,7 +496,8 @@ + COMMON= calendars CONTRIBUTING LICENSE Makefile \ + NEWS README theory.html version + WEB_PAGES= tz-art.html tz-how-to.html tz-link.html +-CHECK_WEB_PAGES=check_tz-art.html check_tz-how-to.html check_tz-link.html ++CHECK_WEB_PAGES=check_theory.html check_tz-art.html \ ++ check_tz-how-to.html check_tz-link.html + DOCS= $(MANS) date.1 $(MANTXTS) $(WEB_PAGES) + PRIMARY_YDATA= africa antarctica asia australasia \ + europe northamerica southamerica +@@ -804,9 +810,10 @@ + touch $@ + + check_web: $(CHECK_WEB_PAGES) ++check_theory.html: theory.html + check_tz-art.html: tz-art.html + check_tz-link.html: tz-link.html +-check_tz-art.html check_tz-link.html: ++check_theory.html check_tz-art.html check_tz-link.html: + $(CURL) -sS --url https://validator.w3.org/nu/ -F out=gnu \ + -F file=@$$(expr $@ : 'check_\(.*\)') -o $@.out && \ + test ! -s $@.out || { cat $@.out; exit 1; } +@@ -840,11 +847,13 @@ + touch $@ + + clean_misc: ++ rm -fr check_*.dir + rm -f *.o *.out $(TIME_T_ALTERNATIVES) \ + check_* core typecheck_* \ + date tzselect version.h zdump zic yearistype libtz.a + clean: clean_misc +- rm -fr *.dir *.zi tzdb-*/ $(TZS_NEW) ++ rm -fr *.dir tzdb-*/ ++ rm -f *.zi $(TZS_NEW) + + maintainer-clean: clean + @echo 'This command is intended for maintainers to use; it' +--- contrib/tzdata/NEWS.orig ++++ contrib/tzdata/NEWS +@@ -1,5 +1,53 @@ + News for the tz database + ++Release 20198 - 2019-03-25 22:01:33 -0700 ++ ++ Briefly: ++ Palestine "springs forward" on 2019-03-30 instead of 2019-03-23. ++ Metlakatla "fell back" to rejoin Alaska Time on 2019-01-20 at 02:00. ++ ++ Changes to past and future timestamps ++ ++ Palestine will not start DST until 2019-03-30, instead of 2019-03-23 as ++ previously predicted. Adjust our prediction by guessing that spring ++ transitions will be between 24 and 30 March, which matches recent practice ++ since 2016. (Thanks to Even Scharning and Tim Parenti.) ++ ++ Metlakatla ended its observance of Pacific standard time, ++ rejoining Alaska Time, on 2019-01-20 at 02:00. (Thanks to Ryan ++ Stanley and Tim Parenti.) ++ ++ Changes to past timestamps ++ ++ Israel observed DST in 1980 (08-02/09-13) and 1984 (05-05/08-25). ++ (Thanks to Alois Treindl and Isaac Starkman.) ++ ++ Changes to time zone abbreviations ++ ++ Etc/UCT is now a backward-compatibility link to Etc/UTC, instead ++ of being a separate zone that generates the abbreviation "UCT", ++ which nowadays is typically a typo. (Problem reported by Isiah ++ Meadows.) ++ ++ Changes to code ++ ++ zic now has an -r option to limit the time range of output data. ++ For example, 'zic -r @1000000000' limits the output data to ++ timestamps starting 1000000000 seconds after the Epoch. ++ This helps shrink output size and can be useful for applications ++ not needing the full timestamp history, such as TZDIST truncation; ++ see Internet RFC 8536 section 5.1. (Inspired by a feature request ++ from Christopher Wong, helped along by bug reports from Wong and ++ from Tim Parenti.) ++ ++ Changes to documentation ++ ++ Mention Internet RFC 8536 (February 2019), which documents TZif. ++ ++ tz-link.html now cites tzdata-meta ++ . ++ ++ + Release 2018i - 2018-12-30 11:05:43 -0800 + + Briefly: +@@ -400,8 +448,9 @@ + downstream parsers do not support it. + + * The build procedure constructs three files vanguard.zi, main.zi, +- and rearguard.zi, one for each format. The files represent the +- same data as closely as the formats allow. These three files ++ and rearguard.zi, one for each format. Although the files ++ represent essentially the same data, they may have minor ++ discrepancies that users are not likely to notice. The files + are intended for downstream data consumers and are not + installed. Zoneinfo parsers that do not support negative SAVE values + should start using rearguard.zi, so that they will be unaffected +--- contrib/tzdata/README.orig ++++ contrib/tzdata/README +@@ -1,7 +1,7 @@ + README for the tz distribution + +-"What time is it?" -- Richard Deacon as The King +-"Any time you want it to be." -- Frank Baxter as The Scientist ++"Where do I set the hands of the clock?" -- Les Tremayne as The King ++"Oh that--you can set them any place you want." -- Frank Baxter as The Scientist + (from the Bell System film "About Time") + + The Time Zone Database (called tz, tzdb or zoneinfo) contains code and +--- contrib/tzdata/africa.orig ++++ contrib/tzdata/africa +@@ -364,6 +364,11 @@ + # See Africa/Lagos. + + # Eritrea ++# See Africa/Nairobi. ++ ++# Eswatini (formerly Swaziland) ++# See Africa/Johannesburg. ++ + # Ethiopia + # See Africa/Nairobi. + # +@@ -1188,7 +1193,7 @@ + 1:30 - SAST 1903 Mar + 2:00 SA SAST + Link Africa/Johannesburg Africa/Maseru # Lesotho +-Link Africa/Johannesburg Africa/Mbabane # Swaziland ++Link Africa/Johannesburg Africa/Mbabane # Eswatini + # + # Marion and Prince Edward Is + # scientific station since 1947 +@@ -1230,9 +1235,6 @@ + 2:00 Sudan CA%sT 2000 Jan 15 12:00 + 3:00 - EAT + +-# Swaziland +-# See Africa/Johannesburg. +- + # Tanzania + # See Africa/Nairobi. + +--- contrib/tzdata/asia.orig ++++ contrib/tzdata/asia +@@ -1620,6 +1620,24 @@ + Rule Zion 1974 only - Oct 13 0:00 0 S + Rule Zion 1975 only - Apr 20 0:00 1:00 D + Rule Zion 1975 only - Aug 31 0:00 0 S ++ ++# From Alois Treindl (2019-03-06): ++# http://www.moin.gov.il/Documents/שעון קיץ/clock-50-years-7-2014.pdf ++# From Isaac Starkman (2019-03-06): ++# Summer time was in that period in 1980 and 1984, see ++# https://www.ynet.co.il/articles/0,7340,L-3951073,00.html ++# You can of course read it in translation. ++# I checked the local newspapers for that years. ++# It started on midnight and end at 01.00 am. ++# From Paul Eggert (2019-03-06): ++# Also see this thread about the moin.gov.il URL: ++# https://mm.icann.org/pipermail/tz/2018-November/027194.html ++Rule Zion 1980 only - Aug 2 0:00 1:00 D ++Rule Zion 1980 only - Sep 13 1:00 0 S ++Rule Zion 1984 only - May 5 0:00 1:00 D ++Rule Zion 1984 only - Aug 25 1:00 0 S ++ ++# From Shanks & Pottenger: + Rule Zion 1985 only - Apr 14 0:00 1:00 D + Rule Zion 1985 only - Sep 15 0:00 0 S + Rule Zion 1986 only - May 18 0:00 1:00 D +@@ -3071,9 +3089,15 @@ + # the official website, though the decree did not specify the exact + # time of the time shift. + # http://www.palestinecabinet.gov.ps/Website/AR/NDecrees/ViewFile.ashx?ID=e7a42ab7-ee23-435a-b9c8-a4f7e81f3817 ++ ++# From Even Scharning (2019-03-23): ++# DST in Palestine will start on 30 March this year, not 23 March as the time ++# zone database predicted. ++# https://ramallah.news/post/123610 + # +-# From Paul Eggert (2018-03-16): +-# For 2016 on, predict spring transitions on March's fourth Saturday at 01:00. ++# From Tim Parenti (2019-03-23): ++# Combining this with the rules observed since 2016, adjust our spring ++# transition guess to Mar Sat>=24. + + # Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S + Rule EgyptAsia 1957 only - May 10 0:00 1:00 S +@@ -3104,7 +3128,7 @@ + Rule Palestine 2013 only - Sep Fri>=21 0:00 0 - + Rule Palestine 2014 2015 - Oct Fri>=21 0:00 0 - + Rule Palestine 2015 only - Mar lastFri 24:00 1:00 S +-Rule Palestine 2016 max - Mar Sat>=22 1:00 1:00 S ++Rule Palestine 2016 max - Mar Sat>=24 1:00 1:00 S + Rule Palestine 2016 max - Oct lastSat 1:00 0 - + + # Zone NAME GMTOFF RULES FORMAT [UNTIL] +@@ -3596,5 +3620,17 @@ + 8:00 - +08 1975 Jun 13 + 7:00 - +07 + ++# From Paul Eggert (2019-02-19): ++# ++# The Ho Chi Minh entry suffices for most purposes as it agrees with all of ++# Vietnam since 1975-06-13. Presumably clocks often changed in south Vietnam ++# in the early 1970s as locations changed hands during the war; however the ++# details are unknown and would likely be too voluminous for this database. ++# ++# For timestamps in north Vietnam back to 1970 (the tzdb cutoff), ++# use Asia/Bangkok; see the VN entries in the file zone1970.tab. ++# For timestamps before 1970, see Asia/Hanoi in the file 'backzone'. ++ ++ + # Yemen + # See Asia/Riyadh. +--- contrib/tzdata/backward.orig ++++ contrib/tzdata/backward +@@ -77,6 +77,7 @@ + Link America/Havana Cuba + Link Africa/Cairo Egypt + Link Europe/Dublin Eire ++Link Etc/UTC Etc/UCT + Link Europe/London Europe/Belfast + Link Europe/Chisinau Europe/Tiraspol + Link Europe/London GB +@@ -111,7 +112,7 @@ + Link Asia/Seoul ROK + Link Asia/Singapore Singapore + Link Europe/Istanbul Turkey +-Link Etc/UCT UCT ++Link Etc/UTC UCT + Link America/Anchorage US/Alaska + Link America/Adak US/Aleutian + Link America/Phoenix US/Arizona +--- contrib/tzdata/backzone.orig ++++ contrib/tzdata/backzone +@@ -204,7 +204,7 @@ + 2:00 1:00 SAST 1944 Mar 19 2:00 + 2:00 - SAST + +-# Swaziland ++# Eswatini (formerly Swaziland) + Zone Africa/Mbabane 2:04:24 - LMT 1903 Mar + 2:00 - SAST + +@@ -625,7 +625,7 @@ + 1:00 - CET 1982 Nov 27 + 1:00 EU CE%sT + +-# Macedonia ++# North Macedonia + Zone Europe/Skopje 1:25:44 - LMT 1884 + 1:00 - CET 1941 Apr 18 23:00 + 1:00 C-Eur CE%sT 1945 May 8 2:00s +--- contrib/tzdata/etcetera.orig ++++ contrib/tzdata/etcetera +@@ -19,7 +19,6 @@ + + Zone Etc/GMT 0 - GMT + Zone Etc/UTC 0 - UTC +-Zone Etc/UCT 0 - UCT + + # The following link uses older naming conventions, + # but it belongs here, not in the file 'backward', +--- contrib/tzdata/europe.orig ++++ contrib/tzdata/europe +@@ -1855,7 +1855,7 @@ + 1:00 Belgium CE%sT 1977 + 1:00 EU CE%sT + +-# Macedonia ++# North Macedonia + # See Europe/Belgrade. + + # Malta +@@ -3359,7 +3359,7 @@ + Link Europe/Belgrade Europe/Ljubljana # Slovenia + Link Europe/Belgrade Europe/Podgorica # Montenegro + Link Europe/Belgrade Europe/Sarajevo # Bosnia and Herzegovina +-Link Europe/Belgrade Europe/Skopje # Macedonia ++Link Europe/Belgrade Europe/Skopje # North Macedonia + Link Europe/Belgrade Europe/Zagreb # Croatia + + # Slovakia +--- contrib/tzdata/leap-seconds.list.orig ++++ contrib/tzdata/leap-seconds.list +@@ -204,10 +204,10 @@ + # current -- the update time stamp, the data and the name of the file + # will not change. + # +-# Updated through IERS Bulletin C56 +-# File expires on: 28 June 2019 ++# Updated through IERS Bulletin C57 ++# File expires on: 28 December 2019 + # +-#@ 3770668800 ++#@ 3786480000 + # + 2272060800 10 # 1 Jan 1972 + 2287785600 11 # 1 Jul 1972 +@@ -252,4 +252,4 @@ + # the hash line is also ignored in the + # computation. + # +-#h 62ca19f6 96a4ae0a 3708451c 9f8693f4 016604eb ++#h 83c68138 d3650221 07dbbbcd 11fcc859 ced1106a +--- contrib/tzdata/leapseconds.orig ++++ contrib/tzdata/leapseconds +@@ -63,7 +63,7 @@ + + # POSIX timestamps for the data in this file: + #updated 1467936000 +-#expires 1561680000 ++#expires 1577491200 + +-# Updated through IERS Bulletin C56 +-# File expires on: 28 June 2019 ++# Updated through IERS Bulletin C57 ++# File expires on: 28 December 2019 +--- contrib/tzdata/northamerica.orig ++++ contrib/tzdata/northamerica +@@ -609,6 +609,15 @@ + # In a 2018-12-11 special election, Metlakatla voted to go back to + # Alaska time (including daylight saving time) starting next year. + # https://www.krbd.org/2018/12/12/metlakatla-to-follow-alaska-standard-time-allow-liquor-sales/ ++# ++# From Ryan Stanley (2019-01-11): ++# The community will be changing back on the 20th of this month... ++# From Tim Parenti (2019-01-11): ++# Per an announcement on the Metlakatla community's official Facebook page, the ++# "fall back" will be on Sunday 2019-01-20 at 02:00: ++# https://www.facebook.com/141055983004923/photos/607150969728753/ ++# So they won't be waiting for Alaska to join them on 2019-03-10, but will ++# rather change their clocks twice in seven weeks. + + # Zone NAME GMTOFF RULES FORMAT [UNTIL] + Zone America/Juneau 15:02:19 - LMT 1867 Oct 19 15:33:32 +@@ -637,7 +646,7 @@ + -8:00 US P%sT 1983 Oct 30 2:00 + -8:00 - PST 2015 Nov 1 2:00 + -9:00 US AK%sT 2018 Nov 4 2:00 +- -8:00 - PST 2019 Mar Sun>=8 3:00 ++ -8:00 - PST 2019 Jan 20 2:00 + -9:00 US AK%sT + Zone America/Yakutat 14:41:05 - LMT 1867 Oct 19 15:12:18 + -9:18:55 - LMT 1900 Aug 20 12:00 +--- contrib/tzdata/theory.html.orig ++++ contrib/tzdata/theory.html +@@ -15,7 +15,7 @@ +
    +
  • Scope of the tz + database
  • +-
  • Names of timezones
  • ++
  • Timezone identifiers
  • +
  • Time zone abbreviations
  • +
  • Accuracy of the tz + database
  • +@@ -107,9 +107,9 @@ + + +
    +-

    Names of timezones

    ++

    Timezone identifiers

    +

    +-Each timezone has a unique name. ++Each timezone has a name that uniquely identifies the timezone. + Inexperienced users are not expected to select these names unaided. + Distributors should provide documentation and/or a simple selection + interface that explains each name via a map or via descriptive text like +@@ -142,10 +142,12 @@ + +

  • + Be robust in the presence of political changes. +- For example, names of countries are ordinarily not used, to avoid ++ For example, names are typically not tied to countries, to avoid + incompatibilities when countries change their name (e.g., +- Zaire→Congo) or when locations change countries (e.g., Hong ++ Swaziland→Eswatini) or when locations change countries (e.g., Hong + Kong from UK colony to China). ++ There is no requirement that every country or national ++ capital must have a timezone name. +
  • +
  • + Be portable to a wide variety of implementations. +@@ -215,13 +217,6 @@ + do not need locations, since local time is not defined there. +
  • +
  • +- There should typically be at least one name for each ISO +- 3166-1 officially assigned two-letter code for an inhabited +- country or territory. +-
  • +-
  • + If all the clocks in a timezone have agreed since 1970, + do not bother to include more than one timezone + even if some of the clocks disagreed before 1970. +@@ -228,6 +223,12 @@ + Otherwise these tables would become annoyingly large. +
  • +
  • ++ If boundaries between regions are fluid, such as during a war or ++ insurrection, do not bother to create a new timezone merely ++ because of yet another boundary change. This helps prevent table ++ bloat and simplifies maintenance. ++
  • ++
  • + If a name is ambiguous, use a less ambiguous alternative; + e.g., many cities are named San José and Georgetown, so + prefer America/Costa_Rica to +@@ -299,29 +300,23 @@ +
+ +

+-The file 'zone1970.tab' lists geographical locations used +-to name timezones. +-It is intended to be an exhaustive list of names for geographic +-regions as described above; this is a subset of the timezones in the data. +-Although a 'zone1970.tab' location's +-longitude +-corresponds to +-its local mean +-time (LMT) offset with one hour for every 15° +-east longitude, this relationship is not exact. ++Guidelines have evolved with time, and names following old versions of ++this guideline might not follow the current version. When guidelines ++have changed, old names continue to be supported. Guideline changes ++have included the following: +

+ +-

+-Older versions of this package used a different naming scheme, +-and these older names are still supported. ++

    ++
  • ++Older versions of this package used a different naming scheme. + See the file 'backward' for most of these older names + (e.g., 'US/Eastern' instead of 'America/New_York'). + The other old-fashioned names still supported are + 'WET', 'CET', 'MET', and + 'EET' (see the file 'europe'). +-

    ++
  • + +-

    ++

  • + Older versions of this package defined legacy names that are + incompatible with the first guideline of location names, but which are + still supported. +@@ -332,6 +327,31 @@ + and the file 'northamerica' defines the legacy names + 'EST5EDT', 'CST6CDT', + 'MST7MDT', and 'PST8PDT'. ++
  • ++ ++
  • ++Older versions of this guideline said that ++there should typically be at least one name for each ISO ++3166-1 officially assigned two-letter code for an inhabited ++country or territory. ++This old guideline has been dropped, as it was not needed to handle ++timestamps correctly and it increased maintenance burden. ++
  • ++
++ ++

++The file 'zone1970.tab' lists geographical locations used ++to name timezones. ++It is intended to be an exhaustive list of names for geographic ++regions as described above; this is a subset of the timezones in the data. ++Although a 'zone1970.tab' location's ++longitude ++corresponds to ++its local mean ++time (LMT) offset with one hour for every 15° ++east longitude, this relationship is not exact. +

+ +

+@@ -983,7 +1003,9 @@ + constrained to be a string containing abbreviations + and numeric data as described above. + The file's format is TZif, +- a timezone information format that contains binary data. ++ a timezone information format that contains binary data; see ++ Internet ++ RFC 8536. + The daylight saving time rules to be used for a + particular timezone are encoded in the + TZif file; the format of the file allows US, +@@ -1166,7 +1188,7 @@ +

    +
  • + A set of timezone names as per +- "Names of timezones" above. ++ "Timezone identifiers" above. +
  • +
  • + Library functions described in "Time and date +@@ -1213,6 +1235,17 @@ + offsets or abbreviations for timestamps, as data entries are often + based on guesswork and these guesses may be corrected or improved. +

    ++ ++

    ++Timezone boundaries are not part of the stable interface. ++For example, even though the Asia/Bangkok timezone ++currently includes Chang Mai, Hanoi, and Phnom Penh, this is not part ++of the stable interface and the timezone can split at any time. ++If a calendar application records a future event in some location other ++than Bangkok by putting "Asia/Bangkok" in the event's record, ++the application should be robust in the presence of timezone splits ++between now and the future time. ++

    + + +
    +--- contrib/tzdata/version.orig ++++ contrib/tzdata/version +@@ -1 +1 @@ +-2018i ++2019a diff --git a/share/security/patches/EN-19:08/tzdata-2019a.patch.asc b/share/security/patches/EN-19:08/tzdata-2019a.patch.asc new file mode 100644 index 0000000000..ea8804921e --- /dev/null +++ b/share/security/patches/EN-19:08/tzdata-2019a.patch.asc @@ -0,0 +1,18 @@ +-----BEGIN PGP SIGNATURE----- + +iQKTBAABCgB9FiEE/A6HiuWv54gCjWNV05eS9J6n5cIFAlzbTt9fFIAAAAAALgAo +aXNzdWVyLWZwckBub3RhdGlvbnMub3BlbnBncC5maWZ0aGhvcnNlbWFuLm5ldEZD +MEU4NzhBRTVBRkU3ODgwMjhENjM1NUQzOTc5MkY0OUVBN0U1QzIACgkQ05eS9J6n +5cLylBAAnxBCTvmO6feqKS+TKuC7duoJ/46iaEss1qLU3Co+C53djVU/tGqdHpaV +ZP69xexBkxSthV+Hkifps8LIaD1rF3KYXWEe7icbVvBeSe5mBX079VgTkGtQFaJm +UgSY7aY7fDh5phDF5XjRIl3XgEbKyBTd8f+pDO0rJ3+LehoAqR5F3UXJEGtNvqXm +ZOGZVZoOxD7+8OqJFH7Zb7O70iLhN5U+FEFnfIPGrYywwDn04BVh0pYMryBcp4sr +SiENYNxoByEpN2oNDWq+JkWfi3woZmQHAMc5xhQgDCNiMma7eIAmANdMkz1Q0Hdy +9Tt5Dt7vYkhbfXLqsmUqGXKqGwasWm5jSUT8sXsJ9+DkMnzAGIg8FrZygYIIn/XO +aO5SLRQujKEltnCTEBJygVsjDQB0ZZUIGBER9njasnedlqBJPPG3Yf16aU6kO/LS +jq8xi/ymskE/kYrl9L1G2yiyR83p6BeZu8L8Y68dH9xGpLPmfcVYLs3yYB1Yb7AN +L3eDPfkF/PFN2rTB41m8NoEwQqkqABaIGDaolU5z/Cvs3sUvdpOhJ1PWLzJTeCYV +eDNbu7NqrbKSa79oE6atLSb+JKyv1zWtqivPVJ47LaL3+LYfwTnRHVBcgmb1RFfO +qvSENI9vGNkYrobxlglSi/av8P9TaD5nAk8xJRk6zn3F6cXLHto= +=6OYE +-----END PGP SIGNATURE----- diff --git a/share/security/patches/EN-19:09/xinstall.patch b/share/security/patches/EN-19:09/xinstall.patch new file mode 100644 index 0000000000..bd4882e772 --- /dev/null +++ b/share/security/patches/EN-19:09/xinstall.patch @@ -0,0 +1,71 @@ +--- usr.bin/xinstall/tests/install_test.sh.orig ++++ usr.bin/xinstall/tests/install_test.sh +@@ -377,6 +377,29 @@ + atf_check install -d dir1/dir2/dir3 + } + ++atf_test_case symbolic_link_relative_absolute_common ++symbolic_link_relative_absolute_common_head() { ++ atf_set "descr" "Verify -l rs with absolute paths having common components" ++} ++symbolic_link_relative_absolute_common_body() { ++ filename=foo.so ++ src_path=lib ++ src_path_prefixed=$PWD/$src_path ++ dest_path=$PWD/libexec/ ++ src_file=$src_path_prefixed/$filename ++ dest_file=$dest_path/$filename ++ ++ atf_check mkdir $src_path_prefixed $dest_path ++ atf_check touch $src_file ++ atf_check install -l sr $src_file $dest_path ++ ++ dest_path_relative=$(readlink $dest_file) ++ src_path_relative="../lib/$filename" ++ if [ "$src_path_relative" != "$dest_path_relative" ]; then ++ atf_fail "unexpected symlink contents ('$src_path_relative' != '$dest_path_relative')" ++ fi ++} ++ + atf_init_test_cases() { + atf_add_test_case copy_to_nonexistent + atf_add_test_case copy_to_nonexistent_safe +@@ -415,5 +438,6 @@ + atf_add_test_case symbolic_link_relative_absolute_source_and_dest1 + atf_add_test_case symbolic_link_relative_absolute_source_and_dest1_double_slash + atf_add_test_case symbolic_link_relative_absolute_source_and_dest2 ++ atf_add_test_case symbolic_link_relative_absolute_common + atf_add_test_case mkdir_simple + } +--- usr.bin/xinstall/xinstall.c.orig ++++ usr.bin/xinstall/xinstall.c +@@ -673,7 +673,7 @@ + } + + if (dolink & LN_RELATIVE) { +- char *to_name_copy, *cp, *d, *s; ++ char *to_name_copy, *cp, *d, *ld, *ls, *s; + + if (*from_name != '/') { + /* this is already a relative link */ +@@ -709,8 +709,19 @@ + free(to_name_copy); + + /* Trim common path components. */ +- for (s = src, d = dst; *s == *d; s++, d++) ++ ls = ld = NULL; ++ for (s = src, d = dst; *s == *d; ls = s, ld = d, s++, d++) + continue; ++ /* ++ * If we didn't end after a directory separator, then we've ++ * falsely matched the last component. For example, if one ++ * invoked install -lrs /lib/foo.so /libexec/ then the source ++ * would terminate just after the separator while the ++ * destination would terminate in the middle of 'libexec', ++ * leading to a full directory getting falsely eaten. ++ */ ++ if ((ls != NULL && *ls != '/') || (ld != NULL && *ld != '/')) ++ s--, d--; + while (*s != '/') + s--, d--; + diff --git a/share/security/patches/EN-19:09/xinstall.patch.asc b/share/security/patches/EN-19:09/xinstall.patch.asc new file mode 100644 index 0000000000..ac57a4093b --- /dev/null +++ b/share/security/patches/EN-19:09/xinstall.patch.asc @@ -0,0 +1,18 @@ +-----BEGIN PGP SIGNATURE----- + +iQKTBAABCgB9FiEE/A6HiuWv54gCjWNV05eS9J6n5cIFAlzbTulfFIAAAAAALgAo +aXNzdWVyLWZwckBub3RhdGlvbnMub3BlbnBncC5maWZ0aGhvcnNlbWFuLm5ldEZD +MEU4NzhBRTVBRkU3ODgwMjhENjM1NUQzOTc5MkY0OUVBN0U1QzIACgkQ05eS9J6n +5cLCnhAApUMnH4ylN0QvYr/1DvbRVYXXijKJ6BgmHb93wXO1hlA+6lt9rvitx6Vy +uhId9HDbb4p2Mt4G9mJS8oV1Sb+DKJ0l9cqtvKqLWqtlrwaNWf5Wnnksh0eE5aH/ +44L6ei4jbX6akDhdHhlhvrtwUupZ5MmlbdL5iM/hxtv8yWim4gV+GD4g0wIKCCEu +sZ4cRSmlVO673e1jGYZ5bTtvP24SSDUKivijjswLXoS3mBmWp4n00o0qVR8WIRqJ +95Dy/RKMOnkPDEpbSj6Gnv4GIO/4W/kHhpD/2Su3ga9dUq7fpwYqJKY9wOrF8qz1 +Mc8UTk3uZyfjmd05Kz7R9uTuv7GbYeWFIwTdLyAo3ImwfZIqPMYZqxE8M815Wvlo +V1PBlkj7v4ZTEm4Z7SsjWUTHz7ILHxChGoBRzQkjS3VcItJniz5XIwh8JvQjYRtv +Oco1N7zhuPHE7AHTo8huH/saGjlv4CgzZlgWpfj8zWNPttKKMgPjei+Wle5AToLH +6Zx4AIYU5RdhG6gLxnjdRaj6xF6+PuRSzrym+2sRy89s0ksCtDLJYK2Ehzz5Uocu +d+UMDsYIzE1bNA1Blj1oEulU7yWBsLAq5WznuoP5WeXaLx8PWcIgUHgtbnlPIIES +vG+WFBxOrB9bW6VjfbHQNFf07/rQQ2M7Q6AB/ycbUrUz3/STfxE= +=qyyv +-----END PGP SIGNATURE----- diff --git a/share/security/patches/EN-19:10/scp.patch b/share/security/patches/EN-19:10/scp.patch new file mode 100644 index 0000000000..ddbd9b7cc4 --- /dev/null +++ b/share/security/patches/EN-19:10/scp.patch @@ -0,0 +1,462 @@ +--- crypto/openssh/scp.1.orig ++++ crypto/openssh/scp.1 +@@ -18,7 +18,7 @@ + .Nd secure copy (remote file copy program) + .Sh SYNOPSIS + .Nm scp +-.Op Fl 346BCpqrv ++.Op Fl 346BCpqrTv + .Op Fl c Ar cipher + .Op Fl F Ar ssh_config + .Op Fl i Ar identity_file +@@ -207,6 +207,16 @@ + The program must understand + .Xr ssh 1 + options. ++.It Fl T ++Disable strict filename checking. ++By default when copying files from a remote host to a local directory ++.Nm ++checks that the received filenames match those requested on the command-line ++to prevent the remote end from sending unexpected or unwanted files. ++Because of differences in how various operating systems and shells interpret ++filename wildcards, these checks may cause wanted files to be rejected. ++This option disables these checks at the expense of fully trusting that ++the server will not send unexpected filenames. + .It Fl v + Verbose mode. + Causes +--- crypto/openssh/scp.c.orig ++++ crypto/openssh/scp.c +@@ -1,4 +1,4 @@ +-/* $OpenBSD: scp.c,v 1.197 2018/06/01 04:31:48 dtucker Exp $ */ ++/* $OpenBSD: scp.c,v 1.204 2019/02/10 11:15:52 djm Exp $ */ + /* + * scp - secure remote copy. This is basically patched BSD rcp which + * uses ssh to do the data transfer (instead of using rcmd). +@@ -94,6 +94,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -375,7 +376,7 @@ + struct passwd *pwd; + uid_t userid; + int errs, remin, remout; +-int pflag, iamremote, iamrecursive, targetshouldbedirectory; ++int Tflag, pflag, iamremote, iamrecursive, targetshouldbedirectory; + + #define CMDNEEDS 64 + char cmd[CMDNEEDS]; /* must hold "rcp -r -p -d\0" */ +@@ -382,7 +383,7 @@ + + int response(void); + void rsource(char *, struct stat *); +-void sink(int, char *[]); ++void sink(int, char *[], const char *); + void source(int, char *[]); + void tolocal(int, char *[]); + void toremote(int, char *[]); +@@ -421,8 +422,9 @@ + addargs(&args, "-oRemoteCommand=none"); + addargs(&args, "-oRequestTTY=no"); + +- fflag = tflag = 0; +- while ((ch = getopt(argc, argv, "dfl:prtvBCc:i:P:q12346S:o:F:")) != -1) ++ fflag = Tflag = tflag = 0; ++ while ((ch = getopt(argc, argv, ++ "dfl:prtTvBCc:i:P:q12346S:o:F:")) != -1) { + switch (ch) { + /* User-visible flags. */ + case '1': +@@ -501,9 +503,13 @@ + setmode(0, O_BINARY); + #endif + break; ++ case 'T': ++ Tflag = 1; ++ break; + default: + usage(); + } ++ } + argc -= optind; + argv += optind; + +@@ -534,7 +540,7 @@ + } + if (tflag) { + /* Receive data. */ +- sink(argc, argv); ++ sink(argc, argv, NULL); + exit(errs != 0); + } + if (argc < 2) +@@ -620,6 +626,253 @@ + return r; + } + ++/* Appends a string to an array; returns 0 on success, -1 on alloc failure */ ++static int ++append(char *cp, char ***ap, size_t *np) ++{ ++ char **tmp; ++ ++ if ((tmp = reallocarray(*ap, *np + 1, sizeof(*tmp))) == NULL) ++ return -1; ++ tmp[(*np)] = cp; ++ (*np)++; ++ *ap = tmp; ++ return 0; ++} ++ ++/* ++ * Finds the start and end of the first brace pair in the pattern. ++ * returns 0 on success or -1 for invalid patterns. ++ */ ++static int ++find_brace(const char *pattern, int *startp, int *endp) ++{ ++ int i; ++ int in_bracket, brace_level; ++ ++ *startp = *endp = -1; ++ in_bracket = brace_level = 0; ++ for (i = 0; i < INT_MAX && *endp < 0 && pattern[i] != '\0'; i++) { ++ switch (pattern[i]) { ++ case '\\': ++ /* skip next character */ ++ if (pattern[i + 1] != '\0') ++ i++; ++ break; ++ case '[': ++ in_bracket = 1; ++ break; ++ case ']': ++ in_bracket = 0; ++ break; ++ case '{': ++ if (in_bracket) ++ break; ++ if (pattern[i + 1] == '}') { ++ /* Protect a single {}, for find(1), like csh */ ++ i++; /* skip */ ++ break; ++ } ++ if (*startp == -1) ++ *startp = i; ++ brace_level++; ++ break; ++ case '}': ++ if (in_bracket) ++ break; ++ if (*startp < 0) { ++ /* Unbalanced brace */ ++ return -1; ++ } ++ if (--brace_level <= 0) ++ *endp = i; ++ break; ++ } ++ } ++ /* unbalanced brackets/braces */ ++ if (*endp < 0 && (*startp >= 0 || in_bracket)) ++ return -1; ++ return 0; ++} ++ ++/* ++ * Assembles and records a successfully-expanded pattern, returns -1 on ++ * alloc failure. ++ */ ++static int ++emit_expansion(const char *pattern, int brace_start, int brace_end, ++ int sel_start, int sel_end, char ***patternsp, size_t *npatternsp) ++{ ++ char *cp; ++ int o = 0, tail_len = strlen(pattern + brace_end + 1); ++ ++ if ((cp = malloc(brace_start + (sel_end - sel_start) + ++ tail_len + 1)) == NULL) ++ return -1; ++ ++ /* Pattern before initial brace */ ++ if (brace_start > 0) { ++ memcpy(cp, pattern, brace_start); ++ o = brace_start; ++ } ++ /* Current braced selection */ ++ if (sel_end - sel_start > 0) { ++ memcpy(cp + o, pattern + sel_start, ++ sel_end - sel_start); ++ o += sel_end - sel_start; ++ } ++ /* Remainder of pattern after closing brace */ ++ if (tail_len > 0) { ++ memcpy(cp + o, pattern + brace_end + 1, tail_len); ++ o += tail_len; ++ } ++ cp[o] = '\0'; ++ if (append(cp, patternsp, npatternsp) != 0) { ++ free(cp); ++ return -1; ++ } ++ return 0; ++} ++ ++/* ++ * Expand the first encountered brace in pattern, appending the expanded ++ * patterns it yielded to the *patternsp array. ++ * ++ * Returns 0 on success or -1 on allocation failure. ++ * ++ * Signals whether expansion was performed via *expanded and whether ++ * pattern was invalid via *invalid. ++ */ ++static int ++brace_expand_one(const char *pattern, char ***patternsp, size_t *npatternsp, ++ int *expanded, int *invalid) ++{ ++ int i; ++ int in_bracket, brace_start, brace_end, brace_level; ++ int sel_start, sel_end; ++ ++ *invalid = *expanded = 0; ++ ++ if (find_brace(pattern, &brace_start, &brace_end) != 0) { ++ *invalid = 1; ++ return 0; ++ } else if (brace_start == -1) ++ return 0; ++ ++ in_bracket = brace_level = 0; ++ for (i = sel_start = brace_start + 1; i < brace_end; i++) { ++ switch (pattern[i]) { ++ case '{': ++ if (in_bracket) ++ break; ++ brace_level++; ++ break; ++ case '}': ++ if (in_bracket) ++ break; ++ brace_level--; ++ break; ++ case '[': ++ in_bracket = 1; ++ break; ++ case ']': ++ in_bracket = 0; ++ break; ++ case '\\': ++ if (i < brace_end - 1) ++ i++; /* skip */ ++ break; ++ } ++ if (pattern[i] == ',' || i == brace_end - 1) { ++ if (in_bracket || brace_level > 0) ++ continue; ++ /* End of a selection, emit an expanded pattern */ ++ ++ /* Adjust end index for last selection */ ++ sel_end = (i == brace_end - 1) ? brace_end : i; ++ if (emit_expansion(pattern, brace_start, brace_end, ++ sel_start, sel_end, patternsp, npatternsp) != 0) ++ return -1; ++ /* move on to the next selection */ ++ sel_start = i + 1; ++ continue; ++ } ++ } ++ if (in_bracket || brace_level > 0) { ++ *invalid = 1; ++ return 0; ++ } ++ /* success */ ++ *expanded = 1; ++ return 0; ++} ++ ++/* Expand braces from pattern. Returns 0 on success, -1 on failure */ ++static int ++brace_expand(const char *pattern, char ***patternsp, size_t *npatternsp) ++{ ++ char *cp, *cp2, **active = NULL, **done = NULL; ++ size_t i, nactive = 0, ndone = 0; ++ int ret = -1, invalid = 0, expanded = 0; ++ ++ *patternsp = NULL; ++ *npatternsp = 0; ++ ++ /* Start the worklist with the original pattern */ ++ if ((cp = strdup(pattern)) == NULL) ++ return -1; ++ if (append(cp, &active, &nactive) != 0) { ++ free(cp); ++ return -1; ++ } ++ while (nactive > 0) { ++ cp = active[nactive - 1]; ++ nactive--; ++ if (brace_expand_one(cp, &active, &nactive, ++ &expanded, &invalid) == -1) { ++ free(cp); ++ goto fail; ++ } ++ if (invalid) ++ fatal("%s: invalid brace pattern \"%s\"", __func__, cp); ++ if (expanded) { ++ /* ++ * Current entry expanded to new entries on the ++ * active list; discard the progenitor pattern. ++ */ ++ free(cp); ++ continue; ++ } ++ /* ++ * Pattern did not expand; append the finename component to ++ * the completed list ++ */ ++ if ((cp2 = strrchr(cp, '/')) != NULL) ++ *cp2++ = '\0'; ++ else ++ cp2 = cp; ++ if (append(xstrdup(cp2), &done, &ndone) != 0) { ++ free(cp); ++ goto fail; ++ } ++ free(cp); ++ } ++ /* success */ ++ *patternsp = done; ++ *npatternsp = ndone; ++ done = NULL; ++ ndone = 0; ++ ret = 0; ++ fail: ++ for (i = 0; i < nactive; i++) ++ free(active[i]); ++ free(active); ++ for (i = 0; i < ndone; i++) ++ free(done[i]); ++ free(done); ++ return ret; ++} ++ + void + toremote(int argc, char **argv) + { +@@ -791,7 +1044,7 @@ + continue; + } + free(bp); +- sink(1, argv + argc - 1); ++ sink(1, argv + argc - 1, src); + (void) close(remin); + remin = remout = -1; + } +@@ -967,7 +1220,7 @@ + (sizeof(type) != 4 && sizeof(type) != 8)) + + void +-sink(int argc, char **argv) ++sink(int argc, char **argv, const char *src) + { + static BUF buffer; + struct stat stb; +@@ -983,6 +1236,8 @@ + unsigned long long ull; + int setimes, targisdir, wrerrno = 0; + char ch, *cp, *np, *targ, *why, *vect[1], buf[2048], visbuf[2048]; ++ char **patterns = NULL; ++ size_t n, npatterns = 0; + struct timeval tv[2]; + + #define atime tv[0] +@@ -1007,10 +1262,18 @@ + (void) atomicio(vwrite, remout, "", 1); + if (stat(targ, &stb) == 0 && S_ISDIR(stb.st_mode)) + targisdir = 1; ++ if (src != NULL && !iamrecursive && !Tflag) { ++ /* ++ * Prepare to try to restrict incoming filenames to match ++ * the requested destination file glob. ++ */ ++ if (brace_expand(src, &patterns, &npatterns) != 0) ++ fatal("%s: could not expand pattern", __func__); ++ } + for (first = 1;; first = 0) { + cp = buf; + if (atomicio(read, remin, cp, 1) != 1) +- return; ++ goto done; + if (*cp++ == '\n') + SCREWUP("unexpected "); + do { +@@ -1036,7 +1299,7 @@ + } + if (buf[0] == 'E') { + (void) atomicio(vwrite, remout, "", 1); +- return; ++ goto done; + } + if (ch == '\n') + *--cp = 0; +@@ -1106,10 +1369,19 @@ + SCREWUP("size out of range"); + size = (off_t)ull; + +- if ((strchr(cp, '/') != NULL) || (strcmp(cp, "..") == 0)) { ++ if (*cp == '\0' || strchr(cp, '/') != NULL || ++ strcmp(cp, ".") == 0 || strcmp(cp, "..") == 0) { + run_err("error: unexpected filename: %s", cp); + exit(1); + } ++ if (npatterns > 0) { ++ for (n = 0; n < npatterns; n++) { ++ if (fnmatch(patterns[n], cp, 0) == 0) ++ break; ++ } ++ if (n >= npatterns) ++ SCREWUP("filename does not match request"); ++ } + if (targisdir) { + static char *namebuf; + static size_t cursize; +@@ -1147,7 +1419,7 @@ + goto bad; + } + vect[0] = xstrdup(np); +- sink(1, vect); ++ sink(1, vect, src); + if (setimes) { + setimes = 0; + if (utimes(vect[0], tv) < 0) +@@ -1268,7 +1540,15 @@ + break; + } + } ++done: ++ for (n = 0; n < npatterns; n++) ++ free(patterns[n]); ++ free(patterns); ++ return; + screwup: ++ for (n = 0; n < npatterns; n++) ++ free(patterns[n]); ++ free(patterns); + run_err("protocol error: %s", why); + exit(1); + } +@@ -1315,7 +1595,7 @@ + usage(void) + { + (void) fprintf(stderr, +- "usage: scp [-346BCpqrv] [-c cipher] [-F ssh_config] [-i identity_file]\n" ++ "usage: scp [-346BCpqrTv] [-c cipher] [-F ssh_config] [-i identity_file]\n" + " [-l limit] [-o ssh_option] [-P port] [-S program] source ... target\n"); + exit(1); + } diff --git a/share/security/patches/EN-19:10/scp.patch.asc b/share/security/patches/EN-19:10/scp.patch.asc new file mode 100644 index 0000000000..831f54aefc --- /dev/null +++ b/share/security/patches/EN-19:10/scp.patch.asc @@ -0,0 +1,18 @@ +-----BEGIN PGP SIGNATURE----- + +iQKTBAABCgB9FiEE/A6HiuWv54gCjWNV05eS9J6n5cIFAlzbTvVfFIAAAAAALgAo +aXNzdWVyLWZwckBub3RhdGlvbnMub3BlbnBncC5maWZ0aGhvcnNlbWFuLm5ldEZD +MEU4NzhBRTVBRkU3ODgwMjhENjM1NUQzOTc5MkY0OUVBN0U1QzIACgkQ05eS9J6n +5cKO+Q//TGSAM8N0dqIAi8AsD08fyJqsza70mF0PUq3w4Why0Se6Lm/XvBddR04N +oCP0dDELlcklB3OAj/TFO0IqnozL5FHsRPuE376bjy8i3mK85LvHbC9vxHPGD69A +OoKTgHAe62TqoSSkmJL66FIxbZlb9hh75k4KP2jyhhyD3o9YwuXPjF4vDbjD04s0 +JW2CjDhv+KnTfhjhL/iM/GTHDUl6upv7rWd/6gaH5hr6XDnfiXkcl0fLvOCndw9l +asyXI/MjwkxxK25PKOX3/SixbiVR7oJTfafo6X/Jmw0ROACn3gxo3jaA9Rp/oGSJ +v5BmH9iimAC3o5B9/r2/NIeY0qfZ2DGA8SxOabeVUSppcfc9IvYFyf0FQB6A5Kb3 +otWga6EGg9LutRGT3MX8DMjJ1CMIUIjWlC/szLMLHXGjw6XzL/VSA8W4A+X017eK +6IKs/EVxK7NIrxl2HX3hRC1Slx0MSqWGB3a9eQ4NY3n3C0medadUolKG6whfx7ru +qqBRavYT1C7JPKgNjWd+1x+fngslxlbBJCn8sSSk3pszvL7qcfJJAJbgwGYg+0t1 +c9VdsdsKZBU+Eqe7rVdDxtiI80AR0j7AN8Ph0j2Zm4Ecd5HIj5DsVAwOB6aXz7Kv ++3dbvvrpkFWIpXtOMQ+Qs6YsuneeMIwl2wZ1bXhLBmEtWk94rro= +=rWoT +-----END PGP SIGNATURE----- diff --git a/share/security/patches/SA-19:03/wpa-11.patch b/share/security/patches/SA-19:03/wpa-11.patch new file mode 100644 index 0000000000..e2ffe0f751 --- /dev/null +++ b/share/security/patches/SA-19:03/wpa-11.patch @@ -0,0 +1,192652 @@ +--- contrib/wpa/CONTRIBUTIONS.orig ++++ contrib/wpa/CONTRIBUTIONS +@@ -29,6 +29,34 @@ + unfortunately be accepted. + + ++The preferred method of submitting the contribution to the project is by ++email to the hostap mailing list: ++hostap@lists.infradead.org ++Note that the list may require subscription before accepting message ++without moderation. You can subscribe to the list at this address: ++http://lists.infradead.org/mailman/listinfo/hostap ++ ++The message should contain an inlined patch against the current ++development branch (i.e., the master branch of ++git://w1.fi/hostap.git). Please make sure the software you use for ++sending the patch does not corrupt whitespace. If that cannot be fixed ++for some reason, it is better to include an attached version of the ++patch file than just send a whitespace damaged version in the message ++body. ++ ++The patches should be separate logical changes rather than doing ++everything in a single patch. In other words, please keep cleanup, new ++features, and bug fixes all in their own patches. Each patch needs a ++commit log that describes the changes (what the changes fix, what ++functionality is added, why the changes are useful, etc.). ++ ++Please try to follow the coding style used in the project. ++ ++In general, the best way of generating a suitable formatted patch file ++is by committing the changes to a cloned git repository and using git ++format-patch. The patch can then be sent, e.g., with git send-email. ++ ++ + History of license and contributions terms + ------------------------------------------ + +@@ -112,7 +140,7 @@ + + Modified BSD license (no advertisement clause): + +-Copyright (c) 2002-2015, Jouni Malinen and contributors ++Copyright (c) 2002-2019, Jouni Malinen and contributors + All Rights Reserved. + + Redistribution and use in source and binary forms, with or without +--- contrib/wpa/COPYING.orig ++++ contrib/wpa/COPYING +@@ -1,7 +1,7 @@ + wpa_supplicant and hostapd + -------------------------- + +-Copyright (c) 2002-2015, Jouni Malinen and contributors ++Copyright (c) 2002-2019, Jouni Malinen and contributors + All Rights Reserved. + + +--- contrib/wpa/README.orig ++++ contrib/wpa/README +@@ -1,7 +1,7 @@ + wpa_supplicant and hostapd + -------------------------- + +-Copyright (c) 2002-2015, Jouni Malinen and contributors ++Copyright (c) 2002-2019, Jouni Malinen and contributors + All Rights Reserved. + + These programs are licensed under the BSD license (the one with +--- contrib/wpa/hostapd/ChangeLog.orig ++++ contrib/wpa/hostapd/ChangeLog +@@ -1,5 +1,188 @@ + ChangeLog for hostapd + ++2019-04-21 - v2.8 ++ * SAE changes ++ - added support for SAE Password Identifier ++ - changed default configuration to enable only group 19 ++ (i.e., disable groups 20, 21, 25, 26 from default configuration) and ++ disable all unsuitable groups completely based on REVmd changes ++ - improved anti-clogging token mechanism and SAE authentication ++ frame processing during heavy CPU load; this mitigates some issues ++ with potential DoS attacks trying to flood an AP with large number ++ of SAE messages ++ - added Finite Cyclic Group field in status code 77 responses ++ - reject use of unsuitable groups based on new implementation guidance ++ in REVmd (allow only FFC groups with prime >= 3072 bits and ECC ++ groups with prime >= 256) ++ - minimize timing and memory use differences in PWE derivation ++ [https://w1.fi/security/2019-1/] (CVE-2019-9494) ++ - fixed confirm message validation in error cases ++ [https://w1.fi/security/2019-3/] (CVE-2019-9496) ++ * EAP-pwd changes ++ - minimize timing and memory use differences in PWE derivation ++ [https://w1.fi/security/2019-2/] (CVE-2019-9495) ++ - verify peer scalar/element ++ [https://w1.fi/security/2019-4/] (CVE-2019-9497 and CVE-2019-9498) ++ - fix message reassembly issue with unexpected fragment ++ [https://w1.fi/security/2019-5/] ++ - enforce rand,mask generation rules more strictly ++ - fix a memory leak in PWE derivation ++ - disallow ECC groups with a prime under 256 bits (groups 25, 26, and ++ 27) ++ * Hotspot 2.0 changes ++ - added support for release number 3 ++ - reject release 2 or newer association without PMF ++ * added support for RSN operating channel validation ++ (CONFIG_OCV=y and configuration parameter ocv=1) ++ * added Multi-AP protocol support ++ * added FTM responder configuration ++ * fixed build with LibreSSL ++ * added FT/RRB workaround for short Ethernet frame padding ++ * fixed KEK2 derivation for FILS+FT ++ * added RSSI-based association rejection from OCE ++ * extended beacon reporting functionality ++ * VLAN changes ++ - allow local VLAN management with remote RADIUS authentication ++ - add WPA/WPA2 passphrase/PSK -based VLAN assignment ++ * OpenSSL: allow systemwide policies to be overridden ++ * extended PEAP to derive EMSK to enable use with ERP/FILS ++ * extended WPS to allow SAE configuration to be added automatically ++ for PSK (wps_cred_add_sae=1) ++ * fixed FT and SA Query Action frame with AP-MLME-in-driver cases ++ * OWE: allow Diffie-Hellman Parameter element to be included with DPP ++ in preparation for DPP protocol extension ++ * RADIUS server: started to accept ERP keyName-NAI as user identity ++ automatically without matching EAP database entry ++ * fixed PTK rekeying with FILS and FT ++ ++2018-12-02 - v2.7 ++ * fixed WPA packet number reuse with replayed messages and key ++ reinstallation ++ [http://w1.fi/security/2017-1/] (CVE-2017-13082) ++ * added support for FILS (IEEE 802.11ai) shared key authentication ++ * added support for OWE (Opportunistic Wireless Encryption, RFC 8110; ++ and transition mode defined by WFA) ++ * added support for DPP (Wi-Fi Device Provisioning Protocol) ++ * FT: ++ - added local generation of PMK-R0/PMK-R1 for FT-PSK ++ (ft_psk_generate_local=1) ++ - replaced inter-AP protocol with a cleaner design that is more ++ easily extensible; this breaks backward compatibility and requires ++ all APs in the ESS to be updated at the same time to maintain FT ++ functionality ++ - added support for wildcard R0KH/R1KH ++ - replaced r0_key_lifetime (minutes) parameter with ++ ft_r0_key_lifetime (seconds) ++ - fixed wpa_psk_file use for FT-PSK ++ - fixed FT-SAE PMKID matching ++ - added expiration to PMK-R0 and PMK-R1 cache ++ - added IEEE VLAN support (including tagged VLANs) ++ - added support for SHA384 based AKM ++ * SAE ++ - fixed some PMKSA caching cases with SAE ++ - added support for configuring SAE password separately of the ++ WPA2 PSK/passphrase ++ - added option to require MFP for SAE associations ++ (sae_require_pmf=1) ++ - fixed PTK and EAPOL-Key integrity and key-wrap algorithm selection ++ for SAE; ++ note: this is not backwards compatible, i.e., both the AP and ++ station side implementations will need to be update at the same ++ time to maintain interoperability ++ - added support for Password Identifier ++ * hostapd_cli: added support for command history and completion ++ * added support for requesting beacon report ++ * large number of other fixes, cleanup, and extensions ++ * added option to configure EAPOL-Key retry limits ++ (wpa_group_update_count and wpa_pairwise_update_count) ++ * removed all PeerKey functionality ++ * fixed nl80211 AP mode configuration regression with Linux 4.15 and ++ newer ++ * added support for using wolfSSL cryptographic library ++ * fixed some 20/40 MHz coexistence cases where the BSS could drop to ++ 20 MHz even when 40 MHz would be allowed ++ * Hotspot 2.0 ++ - added support for setting Venue URL ANQP-element (venue_url) ++ - added support for advertising Hotspot 2.0 operator icons ++ - added support for Roaming Consortium Selection element ++ - added support for Terms and Conditions ++ - added support for OSEN connection in a shared RSN BSS ++ * added support for using OpenSSL 1.1.1 ++ * added EAP-pwd server support for salted passwords ++ ++2016-10-02 - v2.6 ++ * fixed EAP-pwd last fragment validation ++ [http://w1.fi/security/2015-7/] (CVE-2015-5314) ++ * fixed WPS configuration update vulnerability with malformed passphrase ++ [http://w1.fi/security/2016-1/] (CVE-2016-4476) ++ * extended channel switch support for VHT bandwidth changes ++ * added support for configuring new ANQP-elements with ++ anqp_elem=: ++ * fixed Suite B 192-bit AKM to use proper PMK length ++ (note: this makes old releases incompatible with the fixed behavior) ++ * added no_probe_resp_if_max_sta=1 parameter to disable Probe Response ++ frame sending for not-associated STAs if max_num_sta limit has been ++ reached ++ * added option (-S as command line argument) to request all interfaces ++ to be started at the same time ++ * modified rts_threshold and fragm_threshold configuration parameters ++ to allow -1 to be used to disable RTS/fragmentation ++ * EAP-pwd: added support for Brainpool Elliptic Curves ++ (with OpenSSL 1.0.2 and newer) ++ * fixed EAPOL reauthentication after FT protocol run ++ * fixed FTIE generation for 4-way handshake after FT protocol run ++ * fixed and improved various FST operations ++ * TLS server ++ - support SHA384 and SHA512 hashes ++ - support TLS v1.2 signature algorithm with SHA384 and SHA512 ++ - support PKCS #5 v2.0 PBES2 ++ - support PKCS #5 with PKCS #12 style key decryption ++ - minimal support for PKCS #12 ++ - support OCSP stapling (including ocsp_multi) ++ * added support for OpenSSL 1.1 API changes ++ - drop support for OpenSSL 0.9.8 ++ - drop support for OpenSSL 1.0.0 ++ * EAP-PEAP: support fast-connect crypto binding ++ * RADIUS ++ - fix Called-Station-Id to not escape SSID ++ - add Event-Timestamp to all Accounting-Request packets ++ - add Acct-Session-Id to Accounting-On/Off ++ - add Acct-Multi-Session-Id ton Access-Request packets ++ - add Service-Type (= Frames) ++ - allow server to provide PSK instead of passphrase for WPA-PSK ++ Tunnel_password case ++ - update full message for interim accounting updates ++ - add Acct-Delay-Time into Accounting messages ++ - add require_message_authenticator configuration option to require ++ CoA/Disconnect-Request packets to be authenticated ++ * started to postpone WNM-Notification frame sending by 100 ms so that ++ the STA has some more time to configure the key before this frame is ++ received after the 4-way handshake ++ * VHT: added interoperability workaround for 80+80 and 160 MHz channels ++ * extended VLAN support (per-STA vif, etc.) ++ * fixed PMKID derivation with SAE ++ * nl80211 ++ - added support for full station state operations ++ - fix IEEE 802.1X/WEP EAP reauthentication and rekeying to use ++ unencrypted EAPOL frames ++ * added initial MBO support; number of extensions to WNM BSS Transition ++ Management ++ * added initial functionality for location related operations ++ * added assocresp_elements parameter to allow vendor specific elements ++ to be added into (Re)Association Response frames ++ * improved Public Action frame addressing ++ - use Address 3 = wildcard BSSID in GAS response if a query from an ++ unassociated STA used that address ++ - fix TX status processing for Address 3 = wildcard BSSID ++ - add gas_address3 configuration parameter to control Address 3 ++ behavior ++ * added command line parameter -i to override interface parameter in ++ hostapd.conf ++ * added command completion support to hostapd_cli ++ * added passive client taxonomy determination (CONFIG_TAXONOMY=y ++ compile option and "SIGNATURE " control interface command) ++ * number of small fixes ++ + 2015-09-27 - v2.5 + * fixed WPS UPnP vulnerability with HTTP chunked transfer encoding + [http://w1.fi/security/2015-2/] (CVE-2015-4141) +--- contrib/wpa/hostapd/README.orig ++++ contrib/wpa/hostapd/README +@@ -2,7 +2,7 @@ + Authenticator and RADIUS authentication server + ================================================================ + +-Copyright (c) 2002-2015, Jouni Malinen and contributors ++Copyright (c) 2002-2019, Jouni Malinen and contributors + All Rights Reserved. + + This program is licensed under the BSD license (the one with +@@ -70,7 +70,7 @@ + Current hardware/software requirements: + - drivers: + Host AP driver for Prism2/2.5/3. +- (http://hostap.epitest.fi/) ++ (http://w1.fi/hostap-driver.html) + Please note that station firmware version needs to be 1.7.0 or newer + to work in WPA mode. + +@@ -81,8 +81,7 @@ + Any wired Ethernet driver for wired IEEE 802.1X authentication + (experimental code) + +- FreeBSD -current (with some kernel mods that have not yet been +- committed when hostapd v0.3.0 was released) ++ FreeBSD -current + BSD net80211 layer (e.g., Atheros driver) + + +@@ -186,24 +185,14 @@ + the Authentication Server. Other than this, the functionality is similar + to the case with the co-located Authentication Server. + +-Authentication Server and Supplicant +------------------------------------- ++Authentication Server ++--------------------- + + Any RADIUS server supporting EAP should be usable as an IEEE 802.1X + Authentication Server with hostapd Authenticator. FreeRADIUS + (http://www.freeradius.org/) has been successfully tested with hostapd +-Authenticator and both Xsupplicant (http://www.open1x.org) and Windows +-XP Supplicants. EAP/TLS was used with Xsupplicant and +-EAP/MD5-Challenge with Windows XP. ++Authenticator. + +-http://www.missl.cs.umd.edu/wireless/eaptls/ has useful information +-about using EAP/TLS with FreeRADIUS and Xsupplicant (just replace +-Cisco access point with Host AP driver, hostapd daemon, and a Prism2 +-card ;-). http://www.freeradius.org/doc/EAP-MD5.html has information +-about using EAP/MD5 with FreeRADIUS, including instructions for WinXP +-configuration. http://www.denobula.com/EAPTLS.pdf has a HOWTO on +-EAP/TLS use with WinXP Supplicant. +- + Automatic WEP key configuration + ------------------------------- + +@@ -243,8 +232,8 @@ + of IEEE 802.11 working group (http://www.ieee802.org/11/) has worked + to address the flaws of the base standard and has in practice + completed its work in May 2004. The IEEE 802.11i amendment to the IEEE +-802.11 standard was approved in June 2004 and this amendment is likely +-to be published in July 2004. ++802.11 standard was approved in June 2004 and this amendment was ++published in July 2004. + + Wi-Fi Alliance (http://www.wi-fi.org/) used a draft version of the + IEEE 802.11i work (draft 3.0) to define a subset of the security +@@ -251,8 +240,7 @@ + enhancements that can be implemented with existing wlan hardware. This + is called Wi-Fi Protected Access (WPA). This has now become a + mandatory component of interoperability testing and certification done +-by Wi-Fi Alliance. Wi-Fi provides information about WPA at its web +-site (http://www.wi-fi.org/OpenSection/protected_access.asp). ++by Wi-Fi Alliance. + + IEEE 802.11 standard defined wired equivalent privacy (WEP) algorithm + for protecting wireless networks. WEP uses RC4 with 40-bit keys, +--- contrib/wpa/hostapd/README-MULTI-AP.orig ++++ contrib/wpa/hostapd/README-MULTI-AP +@@ -0,0 +1,160 @@ ++hostapd, wpa_supplicant and the Multi-AP Specification ++====================================================== ++ ++This document describes how hostapd and wpa_supplicant can be configured to ++support the Multi-AP Specification. ++ ++Introduction to Multi-AP ++------------------------ ++ ++The Wi-Fi Alliance Multi-AP Specification is the technical specification for ++Wi-Fi CERTIFIED EasyMesh(TM) [1], the Wi-Fi Alliance® certification program for ++Multi-AP. It defines control protocols between Wi-Fi® access points (APs) to ++join them into a network with centralized control and operation. It is targeted ++only at routers (repeaters, gateways, ...), not at clients. Clients are not ++involved at all in the protocols. ++ ++Most of the Multi-AP specification falls outside of the scope of ++hostapd/wpa_supplicant. hostapd/wpa_supplicant is only involved for the items ++summarized below. The rest of the protocol must be implemented by a separate ++daemon, e.g., prplMesh [2]. That daemon also needs to communicate with hostapd, ++e.g., to get a list of associated clients, but this can be done using the normal ++hostapd interfaces. ++ ++hostapd/wpa_supplicant needs to be configured specifically to support: ++- the WPS onboarding process; ++- configuring backhaul links. ++ ++The text below refers to "Multi-AP Specification v1.0" [3]. ++ ++ ++Fronthaul and backhaul links ++---------------------------- ++ ++In a Multi-AP network, the central controller can configure the BSSs on the ++devices that are joined into the network. These are called fronthaul BSSs. ++From the point of view of hostapd, there is nothing special about these ++fronthaul BSSs. ++ ++In addition to fronthaul BSSs, the controller can also configure backhaul ++links. A backhaul link is a link between two access point devices, giving ++internet access to access point devices that don't have a wired link. The ++Multi-AP specification doesn't dictate this, but typically the backhaul link ++will be bridged into a LAN together with (one of) the fronthaul BSS(s) and the ++wired Ethernet ports. ++ ++A backhaul link must be treated specially by hostapd and wpa_supplicant. One ++side of the backhaul link is configured through the Multi-AP protocol as the ++"backhaul STA", i.e., the client side of the link. A backhaul STA is like any ++station and is handled appropriately by wpa_supplicant, but two additional ++features are required. It must send an additional information element in each ++(Re)Association Request frame ([3], section 5.2, paragraph 4). In addition, it ++must use 4-address mode for all frames sent over this link ([3], section 14). ++Therefore, wpa_supplicant must be configured explicitly as the backhaul STA ++role, by setting 'multi_ap_backhaul_sta=1' in the network configuration block ++or when configuring the network profile through the control interface. When ++'multi_ap_backhaul_sta=1', wpa_supplicant includes the Multi-AP IE in ++(Re)Association Request frame and verifies that it is included in the ++(Re)Association Response frame. If it is not, association fails. If it is, ++wpa_supplicant sets 4-address mode for this interface through a driver ++callback. ++ ++The AP side of the backhaul link is called a "backhaul BSS". Such a BSS must ++be handled specially by hostapd, because it must add an additional information ++element in each (Re)Association Response frame, but only to stations that have ++identified themselves as backhaul stations ([3], section 5.2, paragraph 5-6). ++This is important because it is possible to use the same BSS and SSID for ++fronthaul and backhaul at the same time. The additional information element must ++only be used for frames sent to a backhaul STA, not to a normal STA. Also, ++frames sent to a backhaul STA must use 4-address mode, while frames sent to a ++normal STA (fronthaul, when it's a fronthaul and backhaul BSS) must use ++3-address mode. ++ ++A BSS is configured in Multi-AP mode in hostapd by setting the 'multi_ap' ++configuration option to 1 (backhaul BSS), 2 (fronthaul BSS), or 3 ++(simultaneous backhaul and fronthaul BSS). If this option is set, hostapd ++parses the Multi-AP information element in the Association Request frame. If the ++station is a backhaul STA and the BSS is configured as a backhaul BSS, ++hostapd sets up 4-address mode. Since there may be multiple stations connected ++simultaneously, and each of them has a different RA (receiver address), a VLAN ++is created for each backhaul STA and it is automatically added to a bridge. ++This is the same behavior as for WDS, and the relevant option ('bridge' or ++'wds_bridge') applies here as well. ++ ++If 'multi_ap' is 1 (backhaul BSS only), any station that tries to associate ++without the Multi-AP information element will be denied. ++ ++If 'multi_ap' is 2 (fronthaul BSS only), any station that tries to associate ++with the Multi-AP information element will be denied. That is also the only ++difference with 'multi_ap' set to 0: in the latter case, the Multi-AP ++information element is simply ignored. ++ ++In summary, this is the end-to-end behavior for a backhaul BSS (i.e., ++multi_ap_backhaul_sta=1 in wpa_supplicant on STA, and multi_ap=1 or 3 in ++hostapd on AP). Note that point 1 means that hostapd must not be configured ++with WPS support on the backhaul BSS (multi_ap=1). hostapd does not check for ++that. ++ ++1. Backhaul BSS beacons do not advertise WPS support (other than that, nothing ++ Multi-AP specific). ++2. STA sends Authentication frame (nothing Multi-AP specific). ++3. AP sends Authentication frame (nothing Multi-AP specific). ++4. STA sends Association Request frame with Multi-AP IE. ++5. AP sends Association Response frame with Multi-AP IE. ++6. STA and AP both use 4-address mode for Data frames. ++ ++ ++WPS support ++----------- ++ ++WPS requires more special handling. WPS must only be advertised on fronthaul ++BSSs, not on backhaul BSSs, so WPS should not be enabled on a backhaul-only ++BSS in hostapd.conf. The WPS configuration purely works on the fronthaul BSS. ++When a WPS M1 message has an additional subelement that indicates a request for ++a Multi-AP backhaul link, hostapd must not respond with the normal fronthaul ++BSS credentials; instead, it should respond with the (potentially different) ++backhaul BSS credentials. ++ ++To support this, hostapd has the 'multi_ap_backhaul_ssid', ++'multi_ap_backhaul_wpa_psk' and 'multi_ap_backhaul_wpa_passphrase' options. ++When these are set on an BSS with WPS, they are used instead of the normal ++credentials when hostapd receives a WPS M1 message with the Multi-AP IE. Only ++WPA2-Personal is supported in the Multi-AP specification, so there is no need ++to specify authentication or encryption options. For the backhaul credentials, ++per-device PSK is not supported. ++ ++If the BSS is a simultaneous backhaul and fronthaul BSS, there is no need to ++specify the backhaul credentials, since the backhaul and fronthaul credentials ++are identical. ++ ++To enable the Multi-AP backhaul STA feature when it performs WPS, a new ++parameter has been introduced to the WPS_PBC control interface call. When this ++"multi_ap=1" option is set, it adds the Multi-AP backhaul subelement to the ++Association Request frame and the M1 message. It then configures the new network ++profile with 'multi_ap_backhaul_sta=1'. Note that this means that if the AP does ++not follow the Multi-AP specification, wpa_supplicant will fail to associate. ++ ++In summary, this is the end-to-end behavior for WPS of a backhaul link (i.e., ++multi_ap=1 option is given in the wps_pbc call on the STA side, and multi_ap=2 ++and multi_ap_backhaul_ssid and either multi_ap_backhaul_wpa_psk or ++multi_ap_backhaul_wpa_passphrase are set to the credentials of a backhaul BSS ++in hostapd on Registrar AP). ++ ++1. Fronthaul BSS Beacon frames advertise WPS support (nothing Multi-AP ++ specific). ++2. Enrollee sends Authentication frame (nothing Multi-AP specific). ++3. AP sends Authentication frame (nothing Multi-AP specific). ++4. Enrollee sends Association Request frame with Multi-AP IE. ++5. AP sends Association Response frame with Multi-AP IE. ++6. Enrollee sends M1 with additional Multi-AP subelement. ++7. AP sends M8 with backhaul instead of fronthaul credentials. ++8. Enrollee sends Deauthentication frame. ++ ++ ++References ++---------- ++ ++[1] https://www.wi-fi.org/discover-wi-fi/wi-fi-easymesh ++[2] https://github.com/prplfoundation/prplMesh ++[3] https://www.wi-fi.org/file/multi-ap-specification-v10 ++ (requires registration) +--- contrib/wpa/hostapd/config_file.c.orig ++++ contrib/wpa/hostapd/config_file.c +@@ -1,6 +1,6 @@ + /* + * hostapd / Configuration file parser +- * Copyright (c) 2003-2015, Jouni Malinen ++ * Copyright (c) 2003-2018, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. +@@ -14,6 +14,8 @@ + #include "utils/common.h" + #include "utils/uuid.h" + #include "common/ieee802_11_defs.h" ++#include "crypto/sha256.h" ++#include "crypto/tls.h" + #include "drivers/driver.h" + #include "eap_server/eap.h" + #include "radius/radius_client.h" +@@ -35,7 +37,7 @@ + const char *fname) + { + FILE *f; +- char buf[128], *pos, *pos2; ++ char buf[128], *pos, *pos2, *pos3; + int line = 0, vlan_id; + struct hostapd_vlan *vlan; + +@@ -80,7 +82,10 @@ + pos2 = pos; + while (*pos2 != ' ' && *pos2 != '\t' && *pos2 != '\0') + pos2++; +- *pos2 = '\0'; ++ ++ if (*pos2 != '\0') ++ *(pos2++) = '\0'; ++ + if (*pos == '\0' || os_strlen(pos) > IFNAMSIZ) { + wpa_printf(MSG_ERROR, "Invalid VLAN ifname at line %d " + "in '%s'", line, fname); +@@ -88,6 +93,13 @@ + return -1; + } + ++ while (*pos2 == ' ' || *pos2 == '\t') ++ pos2++; ++ pos3 = pos2; ++ while (*pos3 != ' ' && *pos3 != '\t' && *pos3 != '\0') ++ pos3++; ++ *pos3 = '\0'; ++ + vlan = os_zalloc(sizeof(*vlan)); + if (vlan == NULL) { + wpa_printf(MSG_ERROR, "Out of memory while reading " +@@ -97,7 +109,10 @@ + } + + vlan->vlan_id = vlan_id; ++ vlan->vlan_desc.untagged = vlan_id; ++ vlan->vlan_desc.notempty = !!vlan_id; + os_strlcpy(vlan->ifname, pos, sizeof(vlan->ifname)); ++ os_strlcpy(vlan->bridge, pos2, sizeof(vlan->bridge)); + vlan->next = bss->vlan; + bss->vlan = vlan; + } +@@ -109,7 +124,7 @@ + #endif /* CONFIG_NO_VLAN */ + + +-static int hostapd_acl_comp(const void *a, const void *b) ++int hostapd_acl_comp(const void *a, const void *b) + { + const struct mac_acl_entry *aa = a; + const struct mac_acl_entry *bb = b; +@@ -117,6 +132,44 @@ + } + + ++int hostapd_add_acl_maclist(struct mac_acl_entry **acl, int *num, ++ int vlan_id, const u8 *addr) ++{ ++ struct mac_acl_entry *newacl; ++ ++ newacl = os_realloc_array(*acl, *num + 1, sizeof(**acl)); ++ if (!newacl) { ++ wpa_printf(MSG_ERROR, "MAC list reallocation failed"); ++ return -1; ++ } ++ ++ *acl = newacl; ++ os_memcpy((*acl)[*num].addr, addr, ETH_ALEN); ++ os_memset(&(*acl)[*num].vlan_id, 0, sizeof((*acl)[*num].vlan_id)); ++ (*acl)[*num].vlan_id.untagged = vlan_id; ++ (*acl)[*num].vlan_id.notempty = !!vlan_id; ++ (*num)++; ++ ++ return 0; ++} ++ ++ ++void hostapd_remove_acl_mac(struct mac_acl_entry **acl, int *num, ++ const u8 *addr) ++{ ++ int i = 0; ++ ++ while (i < *num) { ++ if (os_memcmp((*acl)[i].addr, addr, ETH_ALEN) == 0) { ++ os_remove_in_array(*acl, *num, sizeof(**acl), i); ++ (*num)--; ++ } else { ++ i++; ++ } ++ } ++} ++ ++ + static int hostapd_config_read_maclist(const char *fname, + struct mac_acl_entry **acl, int *num) + { +@@ -124,12 +177,8 @@ + char buf[128], *pos; + int line = 0; + u8 addr[ETH_ALEN]; +- struct mac_acl_entry *newacl; + int vlan_id; + +- if (!fname) +- return 0; +- + f = fopen(fname, "r"); + if (!f) { + wpa_printf(MSG_ERROR, "MAC list file '%s' not found.", fname); +@@ -137,7 +186,7 @@ + } + + while (fgets(buf, sizeof(buf), f)) { +- int i, rem = 0; ++ int rem = 0; + + line++; + +@@ -167,16 +216,7 @@ + } + + if (rem) { +- i = 0; +- while (i < *num) { +- if (os_memcmp((*acl)[i].addr, addr, ETH_ALEN) == +- 0) { +- os_remove_in_array(*acl, *num, +- sizeof(**acl), i); +- (*num)--; +- } else +- i++; +- } ++ hostapd_remove_acl_mac(acl, num, addr); + continue; + } + vlan_id = 0; +@@ -188,22 +228,16 @@ + if (*pos != '\0') + vlan_id = atoi(pos); + +- newacl = os_realloc_array(*acl, *num + 1, sizeof(**acl)); +- if (newacl == NULL) { +- wpa_printf(MSG_ERROR, "MAC list reallocation failed"); ++ if (hostapd_add_acl_maclist(acl, num, vlan_id, addr) < 0) { + fclose(f); + return -1; + } +- +- *acl = newacl; +- os_memcpy((*acl)[*num].addr, addr, ETH_ALEN); +- (*acl)[*num].vlan_id = vlan_id; +- (*num)++; + } + + fclose(f); + +- qsort(*acl, *num, sizeof(**acl), hostapd_acl_comp); ++ if (*acl) ++ qsort(*acl, *num, sizeof(**acl), hostapd_acl_comp); + + return 0; + } +@@ -210,6 +244,62 @@ + + + #ifdef EAP_SERVER ++ ++static int hostapd_config_eap_user_salted(struct hostapd_eap_user *user, ++ const char *hash, size_t len, ++ char **pos, int line, ++ const char *fname) ++{ ++ char *pos2 = *pos; ++ ++ while (*pos2 != '\0' && *pos2 != ' ' && *pos2 != '\t' && *pos2 != '#') ++ pos2++; ++ ++ if (pos2 - *pos < (int) (2 * (len + 1))) { /* at least 1 byte of salt */ ++ wpa_printf(MSG_ERROR, ++ "Invalid salted %s hash on line %d in '%s'", ++ hash, line, fname); ++ return -1; ++ } ++ ++ user->password = os_malloc(len); ++ if (!user->password) { ++ wpa_printf(MSG_ERROR, ++ "Failed to allocate memory for salted %s hash", ++ hash); ++ return -1; ++ } ++ ++ if (hexstr2bin(*pos, user->password, len) < 0) { ++ wpa_printf(MSG_ERROR, ++ "Invalid salted password on line %d in '%s'", ++ line, fname); ++ return -1; ++ } ++ user->password_len = len; ++ *pos += 2 * len; ++ ++ user->salt_len = (pos2 - *pos) / 2; ++ user->salt = os_malloc(user->salt_len); ++ if (!user->salt) { ++ wpa_printf(MSG_ERROR, ++ "Failed to allocate memory for salted %s hash", ++ hash); ++ return -1; ++ } ++ ++ if (hexstr2bin(*pos, user->salt, user->salt_len) < 0) { ++ wpa_printf(MSG_ERROR, ++ "Invalid salt for password on line %d in '%s'", ++ line, fname); ++ return -1; ++ } ++ ++ *pos = pos2; ++ return 0; ++} ++ ++ + static int hostapd_config_read_eap_user(const char *fname, + struct hostapd_bss_config *conf) + { +@@ -218,9 +308,6 @@ + int line = 0, ret = 0, num_methods; + struct hostapd_eap_user *user = NULL, *tail = NULL, *new_user = NULL; + +- if (!fname) +- return 0; +- + if (os_strncmp(fname, "sqlite:", 7) == 0) { + #ifdef CONFIG_SQLITE + os_free(conf->eap_user_sqlite); +@@ -307,13 +394,12 @@ + goto failed; + } + +- user->identity = os_malloc(pos - start); ++ user->identity = os_memdup(start, pos - start); + if (user->identity == NULL) { + wpa_printf(MSG_ERROR, "Failed to allocate " + "memory for EAP identity"); + goto failed; + } +- os_memcpy(user->identity, start, pos - start); + user->identity_len = pos - start; + + if (pos[0] == '"' && pos[1] == '*') { +@@ -431,13 +517,12 @@ + goto failed; + } + +- user->password = os_malloc(pos - start); ++ user->password = os_memdup(start, pos - start); + if (user->password == NULL) { + wpa_printf(MSG_ERROR, "Failed to allocate " + "memory for EAP password"); + goto failed; + } +- os_memcpy(user->password, start, pos - start); + user->password_len = pos - start; + + pos++; +@@ -466,6 +551,24 @@ + user->password_len = 16; + user->password_hash = 1; + pos = pos2; ++ } else if (os_strncmp(pos, "ssha1:", 6) == 0) { ++ pos += 6; ++ if (hostapd_config_eap_user_salted(user, "sha1", 20, ++ &pos, ++ line, fname) < 0) ++ goto failed; ++ } else if (os_strncmp(pos, "ssha256:", 8) == 0) { ++ pos += 8; ++ if (hostapd_config_eap_user_salted(user, "sha256", 32, ++ &pos, ++ line, fname) < 0) ++ goto failed; ++ } else if (os_strncmp(pos, "ssha512:", 8) == 0) { ++ pos += 8; ++ if (hostapd_config_eap_user_salted(user, "sha512", 64, ++ &pos, ++ line, fname) < 0) ++ goto failed; + } else { + pos2 = pos; + while (*pos2 != '\0' && *pos2 != ' ' && +@@ -517,19 +620,15 @@ + fclose(f); + + if (ret == 0) { +- user = conf->eap_user; +- while (user) { +- struct hostapd_eap_user *prev; +- +- prev = user; +- user = user->next; +- hostapd_config_free_eap_user(prev); +- } ++ hostapd_config_free_eap_users(conf->eap_user); + conf->eap_user = new_user; ++ } else { ++ hostapd_config_free_eap_users(new_user); + } + + return ret; + } ++ + #endif /* EAP_SERVER */ + + +@@ -631,8 +730,7 @@ + } + + +-static int hostapd_parse_das_client(struct hostapd_bss_config *bss, +- const char *val) ++static int hostapd_parse_das_client(struct hostapd_bss_config *bss, char *val) + { + char *secret; + +@@ -640,7 +738,7 @@ + if (secret == NULL) + return -1; + +- secret++; ++ *secret++ = '\0'; + + if (hostapd_parse_ip_addr(val, &bss->radius_das_client_addr)) + return -1; +@@ -680,12 +778,16 @@ + val |= WPA_KEY_MGMT_PSK; + else if (os_strcmp(start, "WPA-EAP") == 0) + val |= WPA_KEY_MGMT_IEEE8021X; +-#ifdef CONFIG_IEEE80211R ++#ifdef CONFIG_IEEE80211R_AP + else if (os_strcmp(start, "FT-PSK") == 0) + val |= WPA_KEY_MGMT_FT_PSK; + else if (os_strcmp(start, "FT-EAP") == 0) + val |= WPA_KEY_MGMT_FT_IEEE8021X; +-#endif /* CONFIG_IEEE80211R */ ++#ifdef CONFIG_SHA384 ++ else if (os_strcmp(start, "FT-EAP-SHA384") == 0) ++ val |= WPA_KEY_MGMT_FT_IEEE8021X_SHA384; ++#endif /* CONFIG_SHA384 */ ++#endif /* CONFIG_IEEE80211R_AP */ + #ifdef CONFIG_IEEE80211W + else if (os_strcmp(start, "WPA-PSK-SHA256") == 0) + val |= WPA_KEY_MGMT_PSK_SHA256; +@@ -706,6 +808,30 @@ + else if (os_strcmp(start, "WPA-EAP-SUITE-B-192") == 0) + val |= WPA_KEY_MGMT_IEEE8021X_SUITE_B_192; + #endif /* CONFIG_SUITEB192 */ ++#ifdef CONFIG_FILS ++ else if (os_strcmp(start, "FILS-SHA256") == 0) ++ val |= WPA_KEY_MGMT_FILS_SHA256; ++ else if (os_strcmp(start, "FILS-SHA384") == 0) ++ val |= WPA_KEY_MGMT_FILS_SHA384; ++#ifdef CONFIG_IEEE80211R_AP ++ else if (os_strcmp(start, "FT-FILS-SHA256") == 0) ++ val |= WPA_KEY_MGMT_FT_FILS_SHA256; ++ else if (os_strcmp(start, "FT-FILS-SHA384") == 0) ++ val |= WPA_KEY_MGMT_FT_FILS_SHA384; ++#endif /* CONFIG_IEEE80211R_AP */ ++#endif /* CONFIG_FILS */ ++#ifdef CONFIG_OWE ++ else if (os_strcmp(start, "OWE") == 0) ++ val |= WPA_KEY_MGMT_OWE; ++#endif /* CONFIG_OWE */ ++#ifdef CONFIG_DPP ++ else if (os_strcmp(start, "DPP") == 0) ++ val |= WPA_KEY_MGMT_DPP; ++#endif /* CONFIG_DPP */ ++#ifdef CONFIG_HS20 ++ else if (os_strcmp(start, "OSEN") == 0) ++ val |= WPA_KEY_MGMT_OSEN; ++#endif /* CONFIG_HS20 */ + else { + wpa_printf(MSG_ERROR, "Line %d: invalid key_mgmt '%s'", + line, start); +@@ -751,17 +877,34 @@ + { + size_t len = os_strlen(val); + +- if (keyidx < 0 || keyidx > 3 || wep->key[keyidx] != NULL) ++ if (keyidx < 0 || keyidx > 3) + return -1; + ++ if (len == 0) { ++ int i, set = 0; ++ ++ bin_clear_free(wep->key[keyidx], wep->len[keyidx]); ++ wep->key[keyidx] = NULL; ++ wep->len[keyidx] = 0; ++ for (i = 0; i < NUM_WEP_KEYS; i++) { ++ if (wep->key[i]) ++ set++; ++ } ++ if (!set) ++ wep->keys_set = 0; ++ return 0; ++ } ++ ++ if (wep->key[keyidx] != NULL) ++ return -1; ++ + if (val[0] == '"') { + if (len < 2 || val[len - 1] != '"') + return -1; + len -= 2; +- wep->key[keyidx] = os_malloc(len); ++ wep->key[keyidx] = os_memdup(val + 1, len); + if (wep->key[keyidx] == NULL) + return -1; +- os_memcpy(wep->key[keyidx], val + 1, len); + wep->len[keyidx] = len; + } else { + if (len & 1) +@@ -974,7 +1117,27 @@ + } + + +-#ifdef CONFIG_IEEE80211R ++#ifdef CONFIG_IEEE80211R_AP ++ ++static int rkh_derive_key(const char *pos, u8 *key, size_t key_len) ++{ ++ u8 oldkey[16]; ++ int ret; ++ ++ if (!hexstr2bin(pos, key, key_len)) ++ return 0; ++ ++ /* Try to use old short key for backwards compatibility */ ++ if (hexstr2bin(pos, oldkey, sizeof(oldkey))) ++ return -1; ++ ++ ret = hmac_sha256_kdf(oldkey, sizeof(oldkey), "FT OLDKEY", NULL, 0, ++ key, key_len); ++ os_memset(oldkey, 0, sizeof(oldkey)); ++ return ret; ++} ++ ++ + static int add_r0kh(struct hostapd_bss_config *bss, char *value) + { + struct ft_remote_r0kh *r0kh; +@@ -1008,7 +1171,7 @@ + os_memcpy(r0kh->id, pos, r0kh->id_len); + + pos = next; +- if (hexstr2bin(pos, r0kh->key, sizeof(r0kh->key))) { ++ if (rkh_derive_key(pos, r0kh->key, sizeof(r0kh->key)) < 0) { + wpa_printf(MSG_ERROR, "Invalid R0KH key: '%s'", pos); + os_free(r0kh); + return -1; +@@ -1053,7 +1216,7 @@ + } + + pos = next; +- if (hexstr2bin(pos, r1kh->key, sizeof(r1kh->key))) { ++ if (rkh_derive_key(pos, r1kh->key, sizeof(r1kh->key)) < 0) { + wpa_printf(MSG_ERROR, "Invalid R1KH key: '%s'", pos); + os_free(r1kh); + return -1; +@@ -1064,7 +1227,7 @@ + + return 0; + } +-#endif /* CONFIG_IEEE80211R */ ++#endif /* CONFIG_IEEE80211R_AP */ + + + #ifdef CONFIG_IEEE80211N +@@ -1081,6 +1244,12 @@ + conf->ht_capab |= HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET; + conf->secondary_channel = 1; + } ++ if (os_strstr(capab, "[HT40+]") && os_strstr(capab, "[HT40-]")) { ++ conf->ht_capab |= HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET; ++ conf->ht40_plus_minus_allowed = 1; ++ } ++ if (!os_strstr(capab, "[HT40+]") && !os_strstr(capab, "[HT40-]")) ++ conf->secondary_channel = 0; + if (os_strstr(capab, "[SMPS-STATIC]")) { + conf->ht_capab &= ~HT_CAP_INFO_SMPS_MASK; + conf->ht_capab |= HT_CAP_INFO_SMPS_STATIC; +@@ -1210,6 +1379,30 @@ + #endif /* CONFIG_IEEE80211AC */ + + ++#ifdef CONFIG_IEEE80211AX ++ ++static u8 find_bit_offset(u8 val) ++{ ++ u8 res = 0; ++ ++ for (; val; val >>= 1) { ++ if (val & 1) ++ break; ++ res++; ++ } ++ ++ return res; ++} ++ ++ ++static u8 set_he_cap(int val, u8 mask) ++{ ++ return (u8) (mask & (val << find_bit_offset(mask))); ++} ++ ++#endif /* CONFIG_IEEE80211AX */ ++ ++ + #ifdef CONFIG_INTERWORKING + static int parse_roaming_consortium(struct hostapd_bss_config *bss, char *pos, + int line) +@@ -1303,6 +1496,44 @@ + } + + ++static int parse_venue_url(struct hostapd_bss_config *bss, char *pos, ++ int line) ++{ ++ char *sep; ++ size_t nlen; ++ struct hostapd_venue_url *url; ++ int ret = -1; ++ ++ sep = os_strchr(pos, ':'); ++ if (!sep) ++ goto fail; ++ *sep++ = '\0'; ++ ++ nlen = os_strlen(sep); ++ if (nlen > 254) ++ goto fail; ++ ++ url = os_realloc_array(bss->venue_url, bss->venue_url_count + 1, ++ sizeof(struct hostapd_venue_url)); ++ if (!url) ++ goto fail; ++ ++ bss->venue_url = url; ++ url = &bss->venue_url[bss->venue_url_count++]; ++ ++ url->venue_number = atoi(pos); ++ url->url_len = nlen; ++ os_memcpy(url->url, sep, nlen); ++ ++ ret = 0; ++fail: ++ if (ret) ++ wpa_printf(MSG_ERROR, "Line %d: Invalid venue_url '%s'", ++ line, pos); ++ return ret; ++} ++ ++ + static int parse_3gpp_cell_net(struct hostapd_bss_config *bss, char *buf, + int line) + { +@@ -1519,6 +1750,54 @@ + } + + ++static int parse_anqp_elem(struct hostapd_bss_config *bss, char *buf, int line) ++{ ++ char *delim; ++ u16 infoid; ++ size_t len; ++ struct wpabuf *payload; ++ struct anqp_element *elem; ++ ++ delim = os_strchr(buf, ':'); ++ if (!delim) ++ return -1; ++ delim++; ++ infoid = atoi(buf); ++ len = os_strlen(delim); ++ if (len & 1) ++ return -1; ++ len /= 2; ++ payload = wpabuf_alloc(len); ++ if (!payload) ++ return -1; ++ if (hexstr2bin(delim, wpabuf_put(payload, len), len) < 0) { ++ wpabuf_free(payload); ++ return -1; ++ } ++ ++ dl_list_for_each(elem, &bss->anqp_elem, struct anqp_element, list) { ++ if (elem->infoid == infoid) { ++ /* Update existing entry */ ++ wpabuf_free(elem->payload); ++ elem->payload = payload; ++ return 0; ++ } ++ } ++ ++ /* Add a new entry */ ++ elem = os_zalloc(sizeof(*elem)); ++ if (!elem) { ++ wpabuf_free(payload); ++ return -1; ++ } ++ elem->infoid = infoid; ++ elem->payload = payload; ++ dl_list_add(&bss->anqp_elem, &elem->list); ++ ++ return 0; ++} ++ ++ + static int parse_qos_map_set(struct hostapd_bss_config *bss, + char *buf, int line) + { +@@ -1805,6 +2084,24 @@ + } + + ++static int hs20_parse_osu_nai2(struct hostapd_bss_config *bss, ++ char *pos, int line) ++{ ++ if (bss->last_osu == NULL) { ++ wpa_printf(MSG_ERROR, "Line %d: Unexpected OSU field", line); ++ return -1; ++ } ++ ++ os_free(bss->last_osu->osu_nai2); ++ bss->last_osu->osu_nai2 = os_strdup(pos); ++ if (bss->last_osu->osu_nai2 == NULL) ++ return -1; ++ bss->hs20_osu_providers_nai_count++; ++ ++ return 0; ++} ++ ++ + static int hs20_parse_osu_method_list(struct hostapd_bss_config *bss, char *pos, + int line) + { +@@ -1864,34 +2161,28 @@ + return 0; + } + +-#endif /* CONFIG_HS20 */ + +- +-#ifdef CONFIG_WPS_NFC +-static struct wpabuf * hostapd_parse_bin(const char *buf) ++static int hs20_parse_operator_icon(struct hostapd_bss_config *bss, char *pos, ++ int line) + { +- size_t len; +- struct wpabuf *ret; ++ char **n; + +- len = os_strlen(buf); +- if (len & 0x01) +- return NULL; +- len /= 2; ++ n = os_realloc_array(bss->hs20_operator_icon, ++ bss->hs20_operator_icon_count + 1, sizeof(char *)); ++ if (!n) ++ return -1; ++ bss->hs20_operator_icon = n; ++ bss->hs20_operator_icon[bss->hs20_operator_icon_count] = os_strdup(pos); ++ if (!bss->hs20_operator_icon[bss->hs20_operator_icon_count]) ++ return -1; ++ bss->hs20_operator_icon_count++; + +- ret = wpabuf_alloc(len); +- if (ret == NULL) +- return NULL; ++ return 0; ++} + +- if (hexstr2bin(buf, wpabuf_put(ret, len), len)) { +- wpabuf_free(ret); +- return NULL; +- } ++#endif /* CONFIG_HS20 */ + +- return ret; +-} +-#endif /* CONFIG_WPS_NFC */ + +- + #ifdef CONFIG_ACS + static int hostapd_config_parse_acs_chan_bias(struct hostapd_config *conf, + char *pos) +@@ -1934,6 +2225,157 @@ + #endif /* CONFIG_ACS */ + + ++static int parse_wpabuf_hex(int line, const char *name, struct wpabuf **buf, ++ const char *val) ++{ ++ struct wpabuf *elems; ++ ++ if (val[0] == '\0') { ++ wpabuf_free(*buf); ++ *buf = NULL; ++ return 0; ++ } ++ ++ elems = wpabuf_parse_bin(val); ++ if (!elems) { ++ wpa_printf(MSG_ERROR, "Line %d: Invalid %s '%s'", ++ line, name, val); ++ return -1; ++ } ++ ++ wpabuf_free(*buf); ++ *buf = elems; ++ ++ return 0; ++} ++ ++ ++#ifdef CONFIG_FILS ++static int parse_fils_realm(struct hostapd_bss_config *bss, const char *val) ++{ ++ struct fils_realm *realm; ++ size_t len; ++ ++ len = os_strlen(val); ++ realm = os_zalloc(sizeof(*realm) + len + 1); ++ if (!realm) ++ return -1; ++ ++ os_memcpy(realm->realm, val, len); ++ if (fils_domain_name_hash(val, realm->hash) < 0) { ++ os_free(realm); ++ return -1; ++ } ++ dl_list_add_tail(&bss->fils_realms, &realm->list); ++ ++ return 0; ++} ++#endif /* CONFIG_FILS */ ++ ++ ++#ifdef EAP_SERVER ++static unsigned int parse_tls_flags(const char *val) ++{ ++ unsigned int flags = 0; ++ ++ /* Disable TLS v1.3 by default for now to avoid interoperability issue. ++ * This can be enabled by default once the implementation has been fully ++ * completed and tested with other implementations. */ ++ flags |= TLS_CONN_DISABLE_TLSv1_3; ++ ++ if (os_strstr(val, "[ALLOW-SIGN-RSA-MD5]")) ++ flags |= TLS_CONN_ALLOW_SIGN_RSA_MD5; ++ if (os_strstr(val, "[DISABLE-TIME-CHECKS]")) ++ flags |= TLS_CONN_DISABLE_TIME_CHECKS; ++ if (os_strstr(val, "[DISABLE-TLSv1.0]")) ++ flags |= TLS_CONN_DISABLE_TLSv1_0; ++ if (os_strstr(val, "[ENABLE-TLSv1.0]")) ++ flags |= TLS_CONN_ENABLE_TLSv1_0; ++ if (os_strstr(val, "[DISABLE-TLSv1.1]")) ++ flags |= TLS_CONN_DISABLE_TLSv1_1; ++ if (os_strstr(val, "[ENABLE-TLSv1.1]")) ++ flags |= TLS_CONN_ENABLE_TLSv1_1; ++ if (os_strstr(val, "[DISABLE-TLSv1.2]")) ++ flags |= TLS_CONN_DISABLE_TLSv1_2; ++ if (os_strstr(val, "[ENABLE-TLSv1.2]")) ++ flags |= TLS_CONN_ENABLE_TLSv1_2; ++ if (os_strstr(val, "[DISABLE-TLSv1.3]")) ++ flags |= TLS_CONN_DISABLE_TLSv1_3; ++ if (os_strstr(val, "[ENABLE-TLSv1.3]")) ++ flags &= ~TLS_CONN_DISABLE_TLSv1_3; ++ if (os_strstr(val, "[SUITEB]")) ++ flags |= TLS_CONN_SUITEB; ++ if (os_strstr(val, "[SUITEB-NO-ECDH]")) ++ flags |= TLS_CONN_SUITEB_NO_ECDH | TLS_CONN_SUITEB; ++ ++ return flags; ++} ++#endif /* EAP_SERVER */ ++ ++ ++#ifdef CONFIG_SAE ++static int parse_sae_password(struct hostapd_bss_config *bss, const char *val) ++{ ++ struct sae_password_entry *pw; ++ const char *pos = val, *pos2, *end = NULL; ++ ++ pw = os_zalloc(sizeof(*pw)); ++ if (!pw) ++ return -1; ++ os_memset(pw->peer_addr, 0xff, ETH_ALEN); /* default to wildcard */ ++ ++ pos2 = os_strstr(pos, "|mac="); ++ if (pos2) { ++ end = pos2; ++ pos2 += 5; ++ if (hwaddr_aton(pos2, pw->peer_addr) < 0) ++ goto fail; ++ pos = pos2 + ETH_ALEN * 3 - 1; ++ } ++ ++ pos2 = os_strstr(pos, "|vlanid="); ++ if (pos2) { ++ if (!end) ++ end = pos2; ++ pos2 += 8; ++ pw->vlan_id = atoi(pos2); ++ } ++ ++ pos2 = os_strstr(pos, "|id="); ++ if (pos2) { ++ if (!end) ++ end = pos2; ++ pos2 += 4; ++ pw->identifier = os_strdup(pos2); ++ if (!pw->identifier) ++ goto fail; ++ } ++ ++ if (!end) { ++ pw->password = os_strdup(val); ++ if (!pw->password) ++ goto fail; ++ } else { ++ pw->password = os_malloc(end - val + 1); ++ if (!pw->password) ++ goto fail; ++ os_memcpy(pw->password, val, end - val); ++ pw->password[end - val] = '\0'; ++ } ++ ++ pw->next = bss->sae_passwords; ++ bss->sae_passwords = pw; ++ ++ return 0; ++fail: ++ str_clear_free(pw->password); ++ os_free(pw->identifier); ++ os_free(pw); ++ return -1; ++} ++#endif /* CONFIG_SAE */ ++ ++ + static int hostapd_config_fill(struct hostapd_config *conf, + struct hostapd_bss_config *bss, + const char *buf, char *pos, int line) +@@ -1949,20 +2391,21 @@ + os_strlcpy(bss->wds_bridge, pos, sizeof(bss->wds_bridge)); + } else if (os_strcmp(buf, "driver") == 0) { + int j; +- /* clear to get error below if setting is invalid */ +- conf->driver = NULL; ++ const struct wpa_driver_ops *driver = NULL; ++ + for (j = 0; wpa_drivers[j]; j++) { + if (os_strcmp(pos, wpa_drivers[j]->name) == 0) { +- conf->driver = wpa_drivers[j]; ++ driver = wpa_drivers[j]; + break; + } + } +- if (conf->driver == NULL) { ++ if (!driver) { + wpa_printf(MSG_ERROR, + "Line %d: invalid/unknown driver '%s'", + line, pos); + return 1; + } ++ conf->driver = driver; + } else if (os_strcmp(buf, "driver_params") == 0) { + os_free(conf->driver_params); + conf->driver_params = os_strdup(pos); +@@ -2006,13 +2449,16 @@ + } else if (os_strcmp(buf, "utf8_ssid") == 0) { + bss->ssid.utf8_ssid = atoi(pos) > 0; + } else if (os_strcmp(buf, "macaddr_acl") == 0) { +- bss->macaddr_acl = atoi(pos); +- if (bss->macaddr_acl != ACCEPT_UNLESS_DENIED && +- bss->macaddr_acl != DENY_UNLESS_ACCEPTED && +- bss->macaddr_acl != USE_EXTERNAL_RADIUS_AUTH) { ++ enum macaddr_acl acl = atoi(pos); ++ ++ if (acl != ACCEPT_UNLESS_DENIED && ++ acl != DENY_UNLESS_ACCEPTED && ++ acl != USE_EXTERNAL_RADIUS_AUTH) { + wpa_printf(MSG_ERROR, "Line %d: unknown macaddr_acl %d", +- line, bss->macaddr_acl); ++ line, acl); ++ return 1; + } ++ bss->macaddr_acl = acl; + } else if (os_strcmp(buf, "accept_mac_file") == 0) { + if (hostapd_config_read_maclist(pos, &bss->accept_mac, + &bss->num_accept_mac)) { +@@ -2039,8 +2485,8 @@ + bss->skip_inactivity_poll = atoi(pos); + } else if (os_strcmp(buf, "country_code") == 0) { + os_memcpy(conf->country, pos, 2); +- /* FIX: make this configurable */ +- conf->country[2] = ' '; ++ } else if (os_strcmp(buf, "country3") == 0) { ++ conf->country[2] = strtol(pos, NULL, 16); + } else if (os_strcmp(buf, "ieee80211d") == 0) { + conf->ieee80211d = atoi(pos); + } else if (os_strcmp(buf, "ieee80211h") == 0) { +@@ -2048,13 +2494,15 @@ + } else if (os_strcmp(buf, "ieee8021x") == 0) { + bss->ieee802_1x = atoi(pos); + } else if (os_strcmp(buf, "eapol_version") == 0) { +- bss->eapol_version = atoi(pos); +- if (bss->eapol_version < 1 || bss->eapol_version > 2) { ++ int eapol_version = atoi(pos); ++ ++ if (eapol_version < 1 || eapol_version > 2) { + wpa_printf(MSG_ERROR, + "Line %d: invalid EAPOL version (%d): '%s'.", +- line, bss->eapol_version, pos); ++ line, eapol_version, pos); + return 1; + } ++ bss->eapol_version = eapol_version; + wpa_printf(MSG_DEBUG, "eapol_version=%d", bss->eapol_version); + #ifdef EAP_SERVER + } else if (os_strcmp(buf, "eap_authenticator") == 0) { +@@ -2077,13 +2525,32 @@ + } else if (os_strcmp(buf, "private_key_passwd") == 0) { + os_free(bss->private_key_passwd); + bss->private_key_passwd = os_strdup(pos); ++ } else if (os_strcmp(buf, "check_cert_subject") == 0) { ++ if (!pos[0]) { ++ wpa_printf(MSG_ERROR, "Line %d: unknown check_cert_subject '%s'", ++ line, pos); ++ return 1; ++ } ++ os_free(bss->check_cert_subject); ++ bss->check_cert_subject = os_strdup(pos); ++ if (!bss->check_cert_subject) ++ return 1; + } else if (os_strcmp(buf, "check_crl") == 0) { + bss->check_crl = atoi(pos); ++ } else if (os_strcmp(buf, "check_crl_strict") == 0) { ++ bss->check_crl_strict = atoi(pos); ++ } else if (os_strcmp(buf, "crl_reload_interval") == 0) { ++ bss->crl_reload_interval = atoi(pos); + } else if (os_strcmp(buf, "tls_session_lifetime") == 0) { + bss->tls_session_lifetime = atoi(pos); ++ } else if (os_strcmp(buf, "tls_flags") == 0) { ++ bss->tls_flags = parse_tls_flags(pos); + } else if (os_strcmp(buf, "ocsp_stapling_response") == 0) { + os_free(bss->ocsp_stapling_response); + bss->ocsp_stapling_response = os_strdup(pos); ++ } else if (os_strcmp(buf, "ocsp_stapling_response_multi") == 0) { ++ os_free(bss->ocsp_stapling_response_multi); ++ bss->ocsp_stapling_response_multi = os_strdup(pos); + } else if (os_strcmp(buf, "dh_file") == 0) { + os_free(bss->dh_file); + bss->dh_file = os_strdup(pos); +@@ -2090,6 +2557,9 @@ + } else if (os_strcmp(buf, "openssl_ciphers") == 0) { + os_free(bss->openssl_ciphers); + bss->openssl_ciphers = os_strdup(pos); ++ } else if (os_strcmp(buf, "openssl_ecdh_curves") == 0) { ++ os_free(bss->openssl_ecdh_curves); ++ bss->openssl_ecdh_curves = os_strdup(pos); + } else if (os_strcmp(buf, "fragment_size") == 0) { + bss->fragment_size = atoi(pos); + #ifdef EAP_SERVER_FAST +@@ -2139,6 +2609,8 @@ + } else if (os_strcmp(buf, "eap_sim_db") == 0) { + os_free(bss->eap_sim_db); + bss->eap_sim_db = os_strdup(pos); ++ } else if (os_strcmp(buf, "eap_sim_db_timeout") == 0) { ++ bss->eap_sim_db_timeout = atoi(pos); + } else if (os_strcmp(buf, "eap_sim_aka_result_ind") == 0) { + bss->eap_sim_aka_result_ind = atoi(pos); + #endif /* EAP_SERVER_SIM */ +@@ -2150,8 +2622,10 @@ + } else if (os_strcmp(buf, "pwd_group") == 0) { + bss->pwd_group = atoi(pos); + #endif /* EAP_SERVER_PWD */ ++#ifdef CONFIG_ERP + } else if (os_strcmp(buf, "eap_server_erp") == 0) { + bss->eap_server_erp = atoi(pos); ++#endif /* CONFIG_ERP */ + #endif /* EAP_SERVER */ + } else if (os_strcmp(buf, "eap_message") == 0) { + char *term; +@@ -2177,24 +2651,25 @@ + os_free(bss->erp_domain); + bss->erp_domain = os_strdup(pos); + } else if (os_strcmp(buf, "wep_key_len_broadcast") == 0) { +- bss->default_wep_key_len = atoi(pos); +- if (bss->default_wep_key_len > 13) { +- wpa_printf(MSG_ERROR, "Line %d: invalid WEP key len %lu (= %lu bits)", +- line, +- (unsigned long) bss->default_wep_key_len, +- (unsigned long) +- bss->default_wep_key_len * 8); ++ int val = atoi(pos); ++ ++ if (val < 0 || val > 13) { ++ wpa_printf(MSG_ERROR, ++ "Line %d: invalid WEP key len %d (= %d bits)", ++ line, val, val * 8); + return 1; + } ++ bss->default_wep_key_len = val; + } else if (os_strcmp(buf, "wep_key_len_unicast") == 0) { +- bss->individual_wep_key_len = atoi(pos); +- if (bss->individual_wep_key_len < 0 || +- bss->individual_wep_key_len > 13) { +- wpa_printf(MSG_ERROR, "Line %d: invalid WEP key len %d (= %d bits)", +- line, bss->individual_wep_key_len, +- bss->individual_wep_key_len * 8); ++ int val = atoi(pos); ++ ++ if (val < 0 || val > 13) { ++ wpa_printf(MSG_ERROR, ++ "Line %d: invalid WEP key len %d (= %d bits)", ++ line, val, val * 8); + return 1; + } ++ bss->individual_wep_key_len = val; + } else if (os_strcmp(buf, "wep_rekey_period") == 0) { + bss->wep_rekeying_period = atoi(pos); + if (bss->wep_rekeying_period < 0) { +@@ -2353,6 +2828,9 @@ + bss->radius_das_time_window = atoi(pos); + } else if (os_strcmp(buf, "radius_das_require_event_timestamp") == 0) { + bss->radius_das_require_event_timestamp = atoi(pos); ++ } else if (os_strcmp(buf, "radius_das_require_message_authenticator") == ++ 0) { ++ bss->radius_das_require_message_authenticator = atoi(pos); + #endif /* CONFIG_NO_RADIUS */ + } else if (os_strcmp(buf, "auth_algs") == 0) { + bss->auth_algs = atoi(pos); +@@ -2373,6 +2851,7 @@ + bss->wpa = atoi(pos); + } else if (os_strcmp(buf, "wpa_group_rekey") == 0) { + bss->wpa_group_rekey = atoi(pos); ++ bss->wpa_group_rekey_set = 1; + } else if (os_strcmp(buf, "wpa_strict_rekey") == 0) { + bss->wpa_strict_rekey = atoi(pos); + } else if (os_strcmp(buf, "wpa_gmk_rekey") == 0) { +@@ -2379,6 +2858,30 @@ + bss->wpa_gmk_rekey = atoi(pos); + } else if (os_strcmp(buf, "wpa_ptk_rekey") == 0) { + bss->wpa_ptk_rekey = atoi(pos); ++ } else if (os_strcmp(buf, "wpa_group_update_count") == 0) { ++ char *endp; ++ unsigned long val = strtoul(pos, &endp, 0); ++ ++ if (*endp || val < 1 || val > (u32) -1) { ++ wpa_printf(MSG_ERROR, ++ "Line %d: Invalid wpa_group_update_count=%lu; allowed range 1..4294967295", ++ line, val); ++ return 1; ++ } ++ bss->wpa_group_update_count = (u32) val; ++ } else if (os_strcmp(buf, "wpa_pairwise_update_count") == 0) { ++ char *endp; ++ unsigned long val = strtoul(pos, &endp, 0); ++ ++ if (*endp || val < 1 || val > (u32) -1) { ++ wpa_printf(MSG_ERROR, ++ "Line %d: Invalid wpa_pairwise_update_count=%lu; allowed range 1..4294967295", ++ line, val); ++ return 1; ++ } ++ bss->wpa_pairwise_update_count = (u32) val; ++ } else if (os_strcmp(buf, "wpa_disable_eapol_key_retries") == 0) { ++ bss->wpa_disable_eapol_key_retries = atoi(pos); + } else if (os_strcmp(buf, "wpa_passphrase") == 0) { + int len = os_strlen(pos); + if (len < 8 || len > 63) { +@@ -2437,7 +2940,7 @@ + if (bss->wpa_pairwise & + (WPA_CIPHER_NONE | WPA_CIPHER_WEP40 | WPA_CIPHER_WEP104)) { + wpa_printf(MSG_ERROR, "Line %d: unsupported pairwise cipher suite '%s'", +- bss->wpa_pairwise, pos); ++ line, pos); + return 1; + } + } else if (os_strcmp(buf, "rsn_pairwise") == 0) { +@@ -2447,9 +2950,23 @@ + if (bss->rsn_pairwise & + (WPA_CIPHER_NONE | WPA_CIPHER_WEP40 | WPA_CIPHER_WEP104)) { + wpa_printf(MSG_ERROR, "Line %d: unsupported pairwise cipher suite '%s'", +- bss->rsn_pairwise, pos); ++ line, pos); + return 1; + } ++ } else if (os_strcmp(buf, "group_cipher") == 0) { ++ bss->group_cipher = hostapd_config_parse_cipher(line, pos); ++ if (bss->group_cipher == -1 || bss->group_cipher == 0) ++ return 1; ++ if (bss->group_cipher != WPA_CIPHER_TKIP && ++ bss->group_cipher != WPA_CIPHER_CCMP && ++ bss->group_cipher != WPA_CIPHER_GCMP && ++ bss->group_cipher != WPA_CIPHER_GCMP_256 && ++ bss->group_cipher != WPA_CIPHER_CCMP_256) { ++ wpa_printf(MSG_ERROR, ++ "Line %d: unsupported group cipher suite '%s'", ++ line, pos); ++ return 1; ++ } + #ifdef CONFIG_RSN_PREAUTH + } else if (os_strcmp(buf, "rsn_preauth") == 0) { + bss->rsn_preauth = atoi(pos); +@@ -2457,11 +2974,10 @@ + os_free(bss->rsn_preauth_interfaces); + bss->rsn_preauth_interfaces = os_strdup(pos); + #endif /* CONFIG_RSN_PREAUTH */ +-#ifdef CONFIG_PEERKEY + } else if (os_strcmp(buf, "peerkey") == 0) { +- bss->peerkey = atoi(pos); +-#endif /* CONFIG_PEERKEY */ +-#ifdef CONFIG_IEEE80211R ++ wpa_printf(MSG_INFO, ++ "Line %d: Obsolete peerkey parameter ignored", line); ++#ifdef CONFIG_IEEE80211R_AP + } else if (os_strcmp(buf, "mobility_domain") == 0) { + if (os_strlen(pos) != 2 * MOBILITY_DOMAIN_ID_LEN || + hexstr2bin(pos, bss->mobility_domain, +@@ -2480,9 +2996,22 @@ + return 1; + } + } else if (os_strcmp(buf, "r0_key_lifetime") == 0) { ++ /* DEPRECATED: Use ft_r0_key_lifetime instead. */ ++ bss->r0_key_lifetime = atoi(pos) * 60; ++ } else if (os_strcmp(buf, "ft_r0_key_lifetime") == 0) { + bss->r0_key_lifetime = atoi(pos); ++ } else if (os_strcmp(buf, "r1_max_key_lifetime") == 0) { ++ bss->r1_max_key_lifetime = atoi(pos); + } else if (os_strcmp(buf, "reassociation_deadline") == 0) { + bss->reassociation_deadline = atoi(pos); ++ } else if (os_strcmp(buf, "rkh_pos_timeout") == 0) { ++ bss->rkh_pos_timeout = atoi(pos); ++ } else if (os_strcmp(buf, "rkh_neg_timeout") == 0) { ++ bss->rkh_neg_timeout = atoi(pos); ++ } else if (os_strcmp(buf, "rkh_pull_timeout") == 0) { ++ bss->rkh_pull_timeout = atoi(pos); ++ } else if (os_strcmp(buf, "rkh_pull_retries") == 0) { ++ bss->rkh_pull_retries = atoi(pos); + } else if (os_strcmp(buf, "r0kh") == 0) { + if (add_r0kh(bss, pos) < 0) { + wpa_printf(MSG_DEBUG, "Line %d: Invalid r0kh '%s'", +@@ -2499,7 +3028,9 @@ + bss->pmk_r1_push = atoi(pos); + } else if (os_strcmp(buf, "ft_over_ds") == 0) { + bss->ft_over_ds = atoi(pos); +-#endif /* CONFIG_IEEE80211R */ ++ } else if (os_strcmp(buf, "ft_psk_generate_local") == 0) { ++ bss->ft_psk_generate_local = atoi(pos); ++#endif /* CONFIG_IEEE80211R_AP */ + #ifndef CONFIG_NO_CTRL_IFACE + } else if (os_strcmp(buf, "ctrl_interface") == 0) { + os_free(bss->ctrl_interface); +@@ -2577,6 +3108,8 @@ + line, pos); + return 1; + } ++ } else if (os_strcmp(buf, "acs_exclude_dfs") == 0) { ++ conf->acs_exclude_dfs = atoi(pos); + } else if (os_strcmp(buf, "channel") == 0) { + if (os_strcmp(pos, "acs_survey") == 0) { + #ifndef CONFIG_ACS +@@ -2603,9 +3136,10 @@ + * cause problems with the current implementation. + * Since it is unlikely that this small numbers are + * useful in real life scenarios, do not allow beacon +- * period to be set below 15 TU. */ +- if (val < 15 || val > 65535) { +- wpa_printf(MSG_ERROR, "Line %d: invalid beacon_int %d (expected 15..65535)", ++ * period to be set below 10 TU. */ ++ if (val < 10 || val > 65535) { ++ wpa_printf(MSG_ERROR, ++ "Line %d: invalid beacon_int %d (expected 10..65535)", + line, val); + return 1; + } +@@ -2627,24 +3161,37 @@ + } + #endif /* CONFIG_ACS */ + } else if (os_strcmp(buf, "dtim_period") == 0) { +- bss->dtim_period = atoi(pos); +- if (bss->dtim_period < 1 || bss->dtim_period > 255) { ++ int val = atoi(pos); ++ ++ if (val < 1 || val > 255) { + wpa_printf(MSG_ERROR, "Line %d: invalid dtim_period %d", +- line, bss->dtim_period); ++ line, val); + return 1; + } ++ bss->dtim_period = val; + } else if (os_strcmp(buf, "bss_load_update_period") == 0) { +- bss->bss_load_update_period = atoi(pos); +- if (bss->bss_load_update_period < 0 || +- bss->bss_load_update_period > 100) { ++ int val = atoi(pos); ++ ++ if (val < 0 || val > 100) { + wpa_printf(MSG_ERROR, + "Line %d: invalid bss_load_update_period %d", +- line, bss->bss_load_update_period); ++ line, val); + return 1; + } ++ bss->bss_load_update_period = val; ++ } else if (os_strcmp(buf, "chan_util_avg_period") == 0) { ++ int val = atoi(pos); ++ ++ if (val < 0) { ++ wpa_printf(MSG_ERROR, ++ "Line %d: invalid chan_util_avg_period", ++ line); ++ return 1; ++ } ++ bss->chan_util_avg_period = val; + } else if (os_strcmp(buf, "rts_threshold") == 0) { + conf->rts_threshold = atoi(pos); +- if (conf->rts_threshold < 0 || conf->rts_threshold > 2347) { ++ if (conf->rts_threshold < -1 || conf->rts_threshold > 65535) { + wpa_printf(MSG_ERROR, + "Line %d: invalid rts_threshold %d", + line, conf->rts_threshold); +@@ -2652,8 +3199,10 @@ + } + } else if (os_strcmp(buf, "fragm_threshold") == 0) { + conf->fragm_threshold = atoi(pos); +- if (conf->fragm_threshold < 256 || +- conf->fragm_threshold > 2346) { ++ if (conf->fragm_threshold == -1) { ++ /* allow a value of -1 */ ++ } else if (conf->fragm_threshold < 256 || ++ conf->fragm_threshold > 2346) { + wpa_printf(MSG_ERROR, + "Line %d: invalid fragm_threshold %d", + line, conf->fragm_threshold); +@@ -2666,7 +3215,7 @@ + line, val); + return 1; + } +- conf->send_probe_response = val; ++ bss->send_probe_response = val; + } else if (os_strcmp(buf, "supported_rates") == 0) { + if (hostapd_parse_intlist(&conf->supported_rates, pos)) { + wpa_printf(MSG_ERROR, "Line %d: invalid rate list", +@@ -2679,6 +3228,40 @@ + line); + return 1; + } ++ } else if (os_strcmp(buf, "beacon_rate") == 0) { ++ int val; ++ ++ if (os_strncmp(pos, "ht:", 3) == 0) { ++ val = atoi(pos + 3); ++ if (val < 0 || val > 31) { ++ wpa_printf(MSG_ERROR, ++ "Line %d: invalid beacon_rate HT-MCS %d", ++ line, val); ++ return 1; ++ } ++ conf->rate_type = BEACON_RATE_HT; ++ conf->beacon_rate = val; ++ } else if (os_strncmp(pos, "vht:", 4) == 0) { ++ val = atoi(pos + 4); ++ if (val < 0 || val > 9) { ++ wpa_printf(MSG_ERROR, ++ "Line %d: invalid beacon_rate VHT-MCS %d", ++ line, val); ++ return 1; ++ } ++ conf->rate_type = BEACON_RATE_VHT; ++ conf->beacon_rate = val; ++ } else { ++ val = atoi(pos); ++ if (val < 10 || val > 10000) { ++ wpa_printf(MSG_ERROR, ++ "Line %d: invalid legacy beacon_rate %d", ++ line, val); ++ return 1; ++ } ++ conf->rate_type = BEACON_RATE_LEGACY; ++ conf->beacon_rate = val; ++ } + } else if (os_strcmp(buf, "preamble") == 0) { + if (atoi(pos)) + conf->preamble = SHORT_PREAMBLE; +@@ -2686,6 +3269,8 @@ + conf->preamble = LONG_PREAMBLE; + } else if (os_strcmp(buf, "ignore_broadcast_ssid") == 0) { + bss->ignore_broadcast_ssid = atoi(pos); ++ } else if (os_strcmp(buf, "no_probe_resp_if_max_sta") == 0) { ++ bss->no_probe_resp_if_max_sta = atoi(pos); + } else if (os_strcmp(buf, "wep_default_key") == 0) { + bss->ssid.wep.idx = atoi(pos); + if (bss->ssid.wep.idx > 3) { +@@ -2707,6 +3292,8 @@ + #ifndef CONFIG_NO_VLAN + } else if (os_strcmp(buf, "dynamic_vlan") == 0) { + bss->ssid.dynamic_vlan = atoi(pos); ++ } else if (os_strcmp(buf, "per_sta_vif") == 0) { ++ bss->ssid.per_sta_vif = atoi(pos); + } else if (os_strcmp(buf, "vlan_file") == 0) { + if (hostapd_config_read_vlan_file(bss, pos)) { + wpa_printf(MSG_ERROR, "Line %d: failed to read VLAN file '%s'", +@@ -2762,6 +3349,8 @@ + line); + return 1; + } ++ } else if (os_strcmp(buf, "use_driver_iface_addr") == 0) { ++ conf->use_driver_iface_addr = atoi(pos); + #ifdef CONFIG_IEEE80211W + } else if (os_strcmp(buf, "ieee80211w") == 0) { + bss->ieee80211w = atoi(pos); +@@ -2794,6 +3383,12 @@ + return 1; + } + #endif /* CONFIG_IEEE80211W */ ++#ifdef CONFIG_OCV ++ } else if (os_strcmp(buf, "ocv") == 0) { ++ bss->ocv = atoi(pos); ++ if (bss->ocv && !bss->ieee80211w) ++ bss->ieee80211w = 1; ++#endif /* CONFIG_OCV */ + #ifdef CONFIG_IEEE80211N + } else if (os_strcmp(buf, "ieee80211n") == 0) { + conf->ieee80211n = atoi(pos); +@@ -2827,7 +3422,111 @@ + conf->vht_oper_centr_freq_seg1_idx = atoi(pos); + } else if (os_strcmp(buf, "vendor_vht") == 0) { + bss->vendor_vht = atoi(pos); ++ } else if (os_strcmp(buf, "use_sta_nsts") == 0) { ++ bss->use_sta_nsts = atoi(pos); + #endif /* CONFIG_IEEE80211AC */ ++#ifdef CONFIG_IEEE80211AX ++ } else if (os_strcmp(buf, "ieee80211ax") == 0) { ++ conf->ieee80211ax = atoi(pos); ++ } else if (os_strcmp(buf, "he_su_beamformer") == 0) { ++ conf->he_phy_capab.he_su_beamformer = atoi(pos); ++ } else if (os_strcmp(buf, "he_su_beamformee") == 0) { ++ conf->he_phy_capab.he_su_beamformee = atoi(pos); ++ } else if (os_strcmp(buf, "he_mu_beamformer") == 0) { ++ conf->he_phy_capab.he_mu_beamformer = atoi(pos); ++ } else if (os_strcmp(buf, "he_bss_color") == 0) { ++ conf->he_op.he_bss_color = atoi(pos); ++ } else if (os_strcmp(buf, "he_default_pe_duration") == 0) { ++ conf->he_op.he_default_pe_duration = atoi(pos); ++ } else if (os_strcmp(buf, "he_twt_required") == 0) { ++ conf->he_op.he_twt_required = atoi(pos); ++ } else if (os_strcmp(buf, "he_rts_threshold") == 0) { ++ conf->he_op.he_rts_threshold = atoi(pos); ++ } else if (os_strcmp(buf, "he_mu_edca_qos_info_param_count") == 0) { ++ conf->he_mu_edca.he_qos_info |= ++ set_he_cap(atoi(pos), HE_QOS_INFO_EDCA_PARAM_SET_COUNT); ++ } else if (os_strcmp(buf, "he_mu_edca_qos_info_q_ack") == 0) { ++ conf->he_mu_edca.he_qos_info |= ++ set_he_cap(atoi(pos), HE_QOS_INFO_Q_ACK); ++ } else if (os_strcmp(buf, "he_mu_edca_qos_info_queue_request") == 0) { ++ conf->he_mu_edca.he_qos_info |= ++ set_he_cap(atoi(pos), HE_QOS_INFO_QUEUE_REQUEST); ++ } else if (os_strcmp(buf, "he_mu_edca_qos_info_txop_request") == 0) { ++ conf->he_mu_edca.he_qos_info |= ++ set_he_cap(atoi(pos), HE_QOS_INFO_TXOP_REQUEST); ++ } else if (os_strcmp(buf, "he_mu_edca_ac_be_aifsn") == 0) { ++ conf->he_mu_edca.he_mu_ac_be_param[HE_MU_AC_PARAM_ACI_IDX] |= ++ set_he_cap(atoi(pos), HE_MU_AC_PARAM_AIFSN); ++ } else if (os_strcmp(buf, "he_mu_edca_ac_be_acm") == 0) { ++ conf->he_mu_edca.he_mu_ac_be_param[HE_MU_AC_PARAM_ACI_IDX] |= ++ set_he_cap(atoi(pos), HE_MU_AC_PARAM_ACM); ++ } else if (os_strcmp(buf, "he_mu_edca_ac_be_aci") == 0) { ++ conf->he_mu_edca.he_mu_ac_be_param[HE_MU_AC_PARAM_ACI_IDX] |= ++ set_he_cap(atoi(pos), HE_MU_AC_PARAM_ACI); ++ } else if (os_strcmp(buf, "he_mu_edca_ac_be_ecwmin") == 0) { ++ conf->he_mu_edca.he_mu_ac_be_param[HE_MU_AC_PARAM_ECW_IDX] |= ++ set_he_cap(atoi(pos), HE_MU_AC_PARAM_ECWMIN); ++ } else if (os_strcmp(buf, "he_mu_edca_ac_be_ecwmax") == 0) { ++ conf->he_mu_edca.he_mu_ac_be_param[HE_MU_AC_PARAM_ECW_IDX] |= ++ set_he_cap(atoi(pos), HE_MU_AC_PARAM_ECWMAX); ++ } else if (os_strcmp(buf, "he_mu_edca_ac_be_timer") == 0) { ++ conf->he_mu_edca.he_mu_ac_be_param[HE_MU_AC_PARAM_TIMER_IDX] = ++ atoi(pos) & 0xff; ++ } else if (os_strcmp(buf, "he_mu_edca_ac_bk_aifsn") == 0) { ++ conf->he_mu_edca.he_mu_ac_bk_param[HE_MU_AC_PARAM_ACI_IDX] |= ++ set_he_cap(atoi(pos), HE_MU_AC_PARAM_AIFSN); ++ } else if (os_strcmp(buf, "he_mu_edca_ac_bk_acm") == 0) { ++ conf->he_mu_edca.he_mu_ac_bk_param[HE_MU_AC_PARAM_ACI_IDX] |= ++ set_he_cap(atoi(pos), HE_MU_AC_PARAM_ACM); ++ } else if (os_strcmp(buf, "he_mu_edca_ac_bk_aci") == 0) { ++ conf->he_mu_edca.he_mu_ac_bk_param[HE_MU_AC_PARAM_ACI_IDX] |= ++ set_he_cap(atoi(pos), HE_MU_AC_PARAM_ACI); ++ } else if (os_strcmp(buf, "he_mu_edca_ac_bk_ecwmin") == 0) { ++ conf->he_mu_edca.he_mu_ac_bk_param[HE_MU_AC_PARAM_ECW_IDX] |= ++ set_he_cap(atoi(pos), HE_MU_AC_PARAM_ECWMIN); ++ } else if (os_strcmp(buf, "he_mu_edca_ac_bk_ecwmax") == 0) { ++ conf->he_mu_edca.he_mu_ac_bk_param[HE_MU_AC_PARAM_ECW_IDX] |= ++ set_he_cap(atoi(pos), HE_MU_AC_PARAM_ECWMAX); ++ } else if (os_strcmp(buf, "he_mu_edca_ac_bk_timer") == 0) { ++ conf->he_mu_edca.he_mu_ac_bk_param[HE_MU_AC_PARAM_TIMER_IDX] = ++ atoi(pos) & 0xff; ++ } else if (os_strcmp(buf, "he_mu_edca_ac_vi_aifsn") == 0) { ++ conf->he_mu_edca.he_mu_ac_vi_param[HE_MU_AC_PARAM_ACI_IDX] |= ++ set_he_cap(atoi(pos), HE_MU_AC_PARAM_AIFSN); ++ } else if (os_strcmp(buf, "he_mu_edca_ac_vi_acm") == 0) { ++ conf->he_mu_edca.he_mu_ac_vi_param[HE_MU_AC_PARAM_ACI_IDX] |= ++ set_he_cap(atoi(pos), HE_MU_AC_PARAM_ACM); ++ } else if (os_strcmp(buf, "he_mu_edca_ac_vi_aci") == 0) { ++ conf->he_mu_edca.he_mu_ac_vi_param[HE_MU_AC_PARAM_ACI_IDX] |= ++ set_he_cap(atoi(pos), HE_MU_AC_PARAM_ACI); ++ } else if (os_strcmp(buf, "he_mu_edca_ac_vi_ecwmin") == 0) { ++ conf->he_mu_edca.he_mu_ac_vi_param[HE_MU_AC_PARAM_ECW_IDX] |= ++ set_he_cap(atoi(pos), HE_MU_AC_PARAM_ECWMIN); ++ } else if (os_strcmp(buf, "he_mu_edca_ac_vi_ecwmax") == 0) { ++ conf->he_mu_edca.he_mu_ac_vi_param[HE_MU_AC_PARAM_ECW_IDX] |= ++ set_he_cap(atoi(pos), HE_MU_AC_PARAM_ECWMAX); ++ } else if (os_strcmp(buf, "he_mu_edca_ac_vi_timer") == 0) { ++ conf->he_mu_edca.he_mu_ac_vi_param[HE_MU_AC_PARAM_TIMER_IDX] = ++ atoi(pos) & 0xff; ++ } else if (os_strcmp(buf, "he_mu_edca_ac_vo_aifsn") == 0) { ++ conf->he_mu_edca.he_mu_ac_vo_param[HE_MU_AC_PARAM_ACI_IDX] |= ++ set_he_cap(atoi(pos), HE_MU_AC_PARAM_AIFSN); ++ } else if (os_strcmp(buf, "he_mu_edca_ac_vo_acm") == 0) { ++ conf->he_mu_edca.he_mu_ac_vo_param[HE_MU_AC_PARAM_ACI_IDX] |= ++ set_he_cap(atoi(pos), HE_MU_AC_PARAM_ACM); ++ } else if (os_strcmp(buf, "he_mu_edca_ac_vo_aci") == 0) { ++ conf->he_mu_edca.he_mu_ac_vo_param[HE_MU_AC_PARAM_ACI_IDX] |= ++ set_he_cap(atoi(pos), HE_MU_AC_PARAM_ACI); ++ } else if (os_strcmp(buf, "he_mu_edca_ac_vo_ecwmin") == 0) { ++ conf->he_mu_edca.he_mu_ac_vo_param[HE_MU_AC_PARAM_ECW_IDX] |= ++ set_he_cap(atoi(pos), HE_MU_AC_PARAM_ECWMIN); ++ } else if (os_strcmp(buf, "he_mu_edca_ac_vo_ecwmax") == 0) { ++ conf->he_mu_edca.he_mu_ac_vo_param[HE_MU_AC_PARAM_ECW_IDX] |= ++ set_he_cap(atoi(pos), HE_MU_AC_PARAM_ECWMAX); ++ } else if (os_strcmp(buf, "he_mu_edca_ac_vo_timer") == 0) { ++ conf->he_mu_edca.he_mu_ac_vo_param[HE_MU_AC_PARAM_TIMER_IDX] = ++ atoi(pos) & 0xff; ++#endif /* CONFIG_IEEE80211AX */ + } else if (os_strcmp(buf, "max_listen_interval") == 0) { + bss->max_listen_interval = atoi(pos); + } else if (os_strcmp(buf, "disable_pmksa_caching") == 0) { +@@ -2908,7 +3607,10 @@ + } + } else if (os_strcmp(buf, "ap_pin") == 0) { + os_free(bss->ap_pin); +- bss->ap_pin = os_strdup(pos); ++ if (*pos == '\0') ++ bss->ap_pin = NULL; ++ else ++ bss->ap_pin = os_strdup(pos); + } else if (os_strcmp(buf, "skip_cred_build") == 0) { + bss->skip_cred_build = atoi(pos); + } else if (os_strcmp(buf, "extra_cred") == 0) { +@@ -2921,6 +3623,8 @@ + } + } else if (os_strcmp(buf, "wps_cred_processing") == 0) { + bss->wps_cred_processing = atoi(pos); ++ } else if (os_strcmp(buf, "wps_cred_add_sae") == 0) { ++ bss->wps_cred_add_sae = atoi(pos); + } else if (os_strcmp(buf, "ap_settings") == 0) { + os_free(bss->ap_settings); + bss->ap_settings = +@@ -2930,6 +3634,56 @@ + line, pos); + return 1; + } ++ } else if (os_strcmp(buf, "multi_ap_backhaul_ssid") == 0) { ++ size_t slen; ++ char *str = wpa_config_parse_string(pos, &slen); ++ ++ if (!str || slen < 1 || slen > SSID_MAX_LEN) { ++ wpa_printf(MSG_ERROR, "Line %d: invalid SSID '%s'", ++ line, pos); ++ os_free(str); ++ return 1; ++ } ++ os_memcpy(bss->multi_ap_backhaul_ssid.ssid, str, slen); ++ bss->multi_ap_backhaul_ssid.ssid_len = slen; ++ bss->multi_ap_backhaul_ssid.ssid_set = 1; ++ os_free(str); ++ } else if (os_strcmp(buf, "multi_ap_backhaul_wpa_passphrase") == 0) { ++ int len = os_strlen(pos); ++ ++ if (len < 8 || len > 63) { ++ wpa_printf(MSG_ERROR, ++ "Line %d: invalid WPA passphrase length %d (expected 8..63)", ++ line, len); ++ return 1; ++ } ++ os_free(bss->multi_ap_backhaul_ssid.wpa_passphrase); ++ bss->multi_ap_backhaul_ssid.wpa_passphrase = os_strdup(pos); ++ if (bss->multi_ap_backhaul_ssid.wpa_passphrase) { ++ hostapd_config_clear_wpa_psk( ++ &bss->multi_ap_backhaul_ssid.wpa_psk); ++ bss->multi_ap_backhaul_ssid.wpa_passphrase_set = 1; ++ } ++ } else if (os_strcmp(buf, "multi_ap_backhaul_wpa_psk") == 0) { ++ hostapd_config_clear_wpa_psk( ++ &bss->multi_ap_backhaul_ssid.wpa_psk); ++ bss->multi_ap_backhaul_ssid.wpa_psk = ++ os_zalloc(sizeof(struct hostapd_wpa_psk)); ++ if (!bss->multi_ap_backhaul_ssid.wpa_psk) ++ return 1; ++ if (hexstr2bin(pos, bss->multi_ap_backhaul_ssid.wpa_psk->psk, ++ PMK_LEN) || ++ pos[PMK_LEN * 2] != '\0') { ++ wpa_printf(MSG_ERROR, "Line %d: Invalid PSK '%s'.", ++ line, pos); ++ hostapd_config_clear_wpa_psk( ++ &bss->multi_ap_backhaul_ssid.wpa_psk); ++ return 1; ++ } ++ bss->multi_ap_backhaul_ssid.wpa_psk->group = 1; ++ os_free(bss->multi_ap_backhaul_ssid.wpa_passphrase); ++ bss->multi_ap_backhaul_ssid.wpa_passphrase = NULL; ++ bss->multi_ap_backhaul_ssid.wpa_psk_set = 1; + } else if (os_strcmp(buf, "upnp_iface") == 0) { + os_free(bss->upnp_iface); + bss->upnp_iface = os_strdup(pos); +@@ -2965,15 +3719,15 @@ + bss->wps_nfc_pw_from_config = 1; + } else if (os_strcmp(buf, "wps_nfc_dh_pubkey") == 0) { + wpabuf_free(bss->wps_nfc_dh_pubkey); +- bss->wps_nfc_dh_pubkey = hostapd_parse_bin(pos); ++ bss->wps_nfc_dh_pubkey = wpabuf_parse_bin(pos); + bss->wps_nfc_pw_from_config = 1; + } else if (os_strcmp(buf, "wps_nfc_dh_privkey") == 0) { + wpabuf_free(bss->wps_nfc_dh_privkey); +- bss->wps_nfc_dh_privkey = hostapd_parse_bin(pos); ++ bss->wps_nfc_dh_privkey = wpabuf_parse_bin(pos); + bss->wps_nfc_pw_from_config = 1; + } else if (os_strcmp(buf, "wps_nfc_dev_pw") == 0) { + wpabuf_free(bss->wps_nfc_dev_pw); +- bss->wps_nfc_dev_pw = hostapd_parse_bin(pos); ++ bss->wps_nfc_dev_pw = wpabuf_parse_bin(pos); + bss->wps_nfc_pw_from_config = 1; + #endif /* CONFIG_WPS_NFC */ + #endif /* CONFIG_WPS */ +@@ -3019,12 +3773,14 @@ + bss->time_zone = os_strdup(pos); + if (bss->time_zone == NULL) + return 1; +-#ifdef CONFIG_WNM ++#ifdef CONFIG_WNM_AP + } else if (os_strcmp(buf, "wnm_sleep_mode") == 0) { + bss->wnm_sleep_mode = atoi(pos); ++ } else if (os_strcmp(buf, "wnm_sleep_mode_no_keys") == 0) { ++ bss->wnm_sleep_mode_no_keys = atoi(pos); + } else if (os_strcmp(buf, "bss_transition") == 0) { + bss->bss_transition = atoi(pos); +-#endif /* CONFIG_WNM */ ++#endif /* CONFIG_WNM_AP */ + #ifdef CONFIG_INTERWORKING + } else if (os_strcmp(buf, "interworking") == 0) { + bss->interworking = atoi(pos); +@@ -3062,6 +3818,9 @@ + } else if (os_strcmp(buf, "venue_name") == 0) { + if (parse_venue_name(bss, pos, line) < 0) + return 1; ++ } else if (os_strcmp(buf, "venue_url") == 0) { ++ if (parse_venue_url(bss, pos, line) < 0) ++ return 1; + } else if (os_strcmp(buf, "network_auth_type") == 0) { + u8 auth_type; + u16 redirect_url_len; +@@ -3136,8 +3895,19 @@ + } else if (os_strcmp(buf, "nai_realm") == 0) { + if (parse_nai_realm(bss, pos, line) < 0) + return 1; ++ } else if (os_strcmp(buf, "anqp_elem") == 0) { ++ if (parse_anqp_elem(bss, pos, line) < 0) ++ return 1; + } else if (os_strcmp(buf, "gas_frag_limit") == 0) { +- bss->gas_frag_limit = atoi(pos); ++ int val = atoi(pos); ++ ++ if (val <= 0) { ++ wpa_printf(MSG_ERROR, ++ "Line %d: Invalid gas_frag_limit '%s'", ++ line, pos); ++ return 1; ++ } ++ bss->gas_frag_limit = val; + } else if (os_strcmp(buf, "gas_comeback_delay") == 0) { + bss->gas_comeback_delay = atoi(pos); + } else if (os_strcmp(buf, "qos_map_set") == 0) { +@@ -3149,13 +3919,25 @@ + os_free(bss->dump_msk_file); + bss->dump_msk_file = os_strdup(pos); + #endif /* CONFIG_RADIUS_TEST */ ++#ifdef CONFIG_PROXYARP ++ } else if (os_strcmp(buf, "proxy_arp") == 0) { ++ bss->proxy_arp = atoi(pos); ++#endif /* CONFIG_PROXYARP */ + #ifdef CONFIG_HS20 + } else if (os_strcmp(buf, "hs20") == 0) { + bss->hs20 = atoi(pos); ++ } else if (os_strcmp(buf, "hs20_release") == 0) { ++ int val = atoi(pos); ++ ++ if (val < 1 || val > (HS20_VERSION >> 4) + 1) { ++ wpa_printf(MSG_ERROR, ++ "Line %d: Unsupported hs20_release: %s", ++ line, pos); ++ return 1; ++ } ++ bss->hs20_release = val; + } else if (os_strcmp(buf, "disable_dgaf") == 0) { + bss->disable_dgaf = atoi(pos); +- } else if (os_strcmp(buf, "proxy_arp") == 0) { +- bss->proxy_arp = atoi(pos); + } else if (os_strcmp(buf, "na_mcast_to_ucast") == 0) { + bss->na_mcast_to_ucast = atoi(pos); + } else if (os_strcmp(buf, "osen") == 0) { +@@ -3216,6 +3998,9 @@ + } else if (os_strcmp(buf, "osu_nai") == 0) { + if (hs20_parse_osu_nai(bss, pos, line) < 0) + return 1; ++ } else if (os_strcmp(buf, "osu_nai2") == 0) { ++ if (hs20_parse_osu_nai2(bss, pos, line) < 0) ++ return 1; + } else if (os_strcmp(buf, "osu_method_list") == 0) { + if (hs20_parse_osu_method_list(bss, pos, line) < 0) + return 1; +@@ -3225,12 +4010,34 @@ + } else if (os_strcmp(buf, "osu_service_desc") == 0) { + if (hs20_parse_osu_service_desc(bss, pos, line) < 0) + return 1; ++ } else if (os_strcmp(buf, "operator_icon") == 0) { ++ if (hs20_parse_operator_icon(bss, pos, line) < 0) ++ return 1; + } else if (os_strcmp(buf, "subscr_remediation_url") == 0) { + os_free(bss->subscr_remediation_url); + bss->subscr_remediation_url = os_strdup(pos); + } else if (os_strcmp(buf, "subscr_remediation_method") == 0) { + bss->subscr_remediation_method = atoi(pos); ++ } else if (os_strcmp(buf, "hs20_t_c_filename") == 0) { ++ os_free(bss->t_c_filename); ++ bss->t_c_filename = os_strdup(pos); ++ } else if (os_strcmp(buf, "hs20_t_c_timestamp") == 0) { ++ bss->t_c_timestamp = strtol(pos, NULL, 0); ++ } else if (os_strcmp(buf, "hs20_t_c_server_url") == 0) { ++ os_free(bss->t_c_server_url); ++ bss->t_c_server_url = os_strdup(pos); ++ } else if (os_strcmp(buf, "hs20_sim_provisioning_url") == 0) { ++ os_free(bss->hs20_sim_provisioning_url); ++ bss->hs20_sim_provisioning_url = os_strdup(pos); + #endif /* CONFIG_HS20 */ ++#ifdef CONFIG_MBO ++ } else if (os_strcmp(buf, "mbo") == 0) { ++ bss->mbo_enabled = atoi(pos); ++ } else if (os_strcmp(buf, "mbo_cell_data_conn_pref") == 0) { ++ bss->mbo_cell_data_conn_pref = atoi(pos); ++ } else if (os_strcmp(buf, "oce") == 0) { ++ bss->oce = atoi(pos); ++#endif /* CONFIG_MBO */ + #ifdef CONFIG_TESTING_OPTIONS + #define PARSE_TEST_PROBABILITY(_val) \ + } else if (os_strcmp(buf, #_val) == 0) { \ +@@ -3249,6 +4056,8 @@ + PARSE_TEST_PROBABILITY(ignore_assoc_probability) + PARSE_TEST_PROBABILITY(ignore_reassoc_probability) + PARSE_TEST_PROBABILITY(corrupt_gtk_rekey_mic_probability) ++ } else if (os_strcmp(buf, "ecsa_ie_only") == 0) { ++ conf->ecsa_ie_only = atoi(pos); + } else if (os_strcmp(buf, "bss_load_test") == 0) { + WPA_PUT_LE16(bss->bss_load_test, atoi(pos)); + pos = os_strchr(pos, ':'); +@@ -3269,7 +4078,15 @@ + WPA_PUT_LE16(&bss->bss_load_test[3], atoi(pos)); + bss->bss_load_test_set = 1; + } else if (os_strcmp(buf, "radio_measurements") == 0) { +- bss->radio_measurements = atoi(pos); ++ /* ++ * DEPRECATED: This parameter will be removed in the future. ++ * Use rrm_neighbor_report instead. ++ */ ++ int val = atoi(pos); ++ ++ if (val & BIT(0)) ++ bss->radio_measurements[0] |= ++ WLAN_RRM_CAPS_NEIGHBOR_REPORT; + } else if (os_strcmp(buf, "own_ie_override") == 0) { + struct wpabuf *tmp; + size_t len = os_strlen(pos) / 2; +@@ -3288,39 +4105,30 @@ + + wpabuf_free(bss->own_ie_override); + bss->own_ie_override = tmp; ++ } else if (os_strcmp(buf, "sae_reflection_attack") == 0) { ++ bss->sae_reflection_attack = atoi(pos); ++ } else if (os_strcmp(buf, "sae_commit_override") == 0) { ++ wpabuf_free(bss->sae_commit_override); ++ bss->sae_commit_override = wpabuf_parse_bin(pos); + #endif /* CONFIG_TESTING_OPTIONS */ +- } else if (os_strcmp(buf, "vendor_elements") == 0) { +- struct wpabuf *elems; +- size_t len = os_strlen(pos); +- if (len & 0x01) { +- wpa_printf(MSG_ERROR, +- "Line %d: Invalid vendor_elements '%s'", +- line, pos); ++#ifdef CONFIG_SAE ++ } else if (os_strcmp(buf, "sae_password") == 0) { ++ if (parse_sae_password(bss, pos) < 0) { ++ wpa_printf(MSG_ERROR, "Line %d: Invalid sae_password", ++ line); + return 1; + } +- len /= 2; +- if (len == 0) { +- wpabuf_free(bss->vendor_elements); +- bss->vendor_elements = NULL; +- return 0; +- } +- +- elems = wpabuf_alloc(len); +- if (elems == NULL) ++#endif /* CONFIG_SAE */ ++ } else if (os_strcmp(buf, "vendor_elements") == 0) { ++ if (parse_wpabuf_hex(line, buf, &bss->vendor_elements, pos)) + return 1; +- +- if (hexstr2bin(pos, wpabuf_put(elems, len), len)) { +- wpabuf_free(elems); +- wpa_printf(MSG_ERROR, +- "Line %d: Invalid vendor_elements '%s'", +- line, pos); ++ } else if (os_strcmp(buf, "assocresp_elements") == 0) { ++ if (parse_wpabuf_hex(line, buf, &bss->assocresp_elements, pos)) + return 1; +- } +- +- wpabuf_free(bss->vendor_elements); +- bss->vendor_elements = elems; + } else if (os_strcmp(buf, "sae_anti_clogging_threshold") == 0) { + bss->sae_anti_clogging_threshold = atoi(pos); ++ } else if (os_strcmp(buf, "sae_sync") == 0) { ++ bss->sae_sync = atoi(pos); + } else if (os_strcmp(buf, "sae_groups") == 0) { + if (hostapd_parse_intlist(&bss->sae_groups, pos)) { + wpa_printf(MSG_ERROR, +@@ -3328,6 +4136,8 @@ + line, pos); + return 1; + } ++ } else if (os_strcmp(buf, "sae_require_mfp") == 0) { ++ bss->sae_require_mfp = atoi(pos); + } else if (os_strcmp(buf, "local_pwr_constraint") == 0) { + int val = atoi(pos); + if (val < 0 || val > 255) { +@@ -3391,7 +4201,8 @@ + return -1; + } + val = strtol(pos, &endp, 0); +- if (*endp || val < 1 || val > FST_MAX_LLT_MS) { ++ if (*endp || val < 1 || ++ (unsigned long int) val > FST_MAX_LLT_MS) { + wpa_printf(MSG_ERROR, + "Line %d: Invalid fst_llt %ld (%s) (expected 1..%u)", + line, val, pos, FST_MAX_LLT_MS); +@@ -3409,6 +4220,135 @@ + } else if (os_strcmp(buf, "no_auth_if_seen_on") == 0) { + os_free(bss->no_auth_if_seen_on); + bss->no_auth_if_seen_on = os_strdup(pos); ++ } else if (os_strcmp(buf, "lci") == 0) { ++ wpabuf_free(conf->lci); ++ conf->lci = wpabuf_parse_bin(pos); ++ if (conf->lci && wpabuf_len(conf->lci) == 0) { ++ wpabuf_free(conf->lci); ++ conf->lci = NULL; ++ } ++ } else if (os_strcmp(buf, "civic") == 0) { ++ wpabuf_free(conf->civic); ++ conf->civic = wpabuf_parse_bin(pos); ++ if (conf->civic && wpabuf_len(conf->civic) == 0) { ++ wpabuf_free(conf->civic); ++ conf->civic = NULL; ++ } ++ } else if (os_strcmp(buf, "rrm_neighbor_report") == 0) { ++ if (atoi(pos)) ++ bss->radio_measurements[0] |= ++ WLAN_RRM_CAPS_NEIGHBOR_REPORT; ++ } else if (os_strcmp(buf, "rrm_beacon_report") == 0) { ++ if (atoi(pos)) ++ bss->radio_measurements[0] |= ++ WLAN_RRM_CAPS_BEACON_REPORT_PASSIVE | ++ WLAN_RRM_CAPS_BEACON_REPORT_ACTIVE | ++ WLAN_RRM_CAPS_BEACON_REPORT_TABLE; ++ } else if (os_strcmp(buf, "gas_address3") == 0) { ++ bss->gas_address3 = atoi(pos); ++ } else if (os_strcmp(buf, "stationary_ap") == 0) { ++ conf->stationary_ap = atoi(pos); ++ } else if (os_strcmp(buf, "ftm_responder") == 0) { ++ bss->ftm_responder = atoi(pos); ++ } else if (os_strcmp(buf, "ftm_initiator") == 0) { ++ bss->ftm_initiator = atoi(pos); ++#ifdef CONFIG_FILS ++ } else if (os_strcmp(buf, "fils_cache_id") == 0) { ++ if (hexstr2bin(pos, bss->fils_cache_id, FILS_CACHE_ID_LEN)) { ++ wpa_printf(MSG_ERROR, ++ "Line %d: Invalid fils_cache_id '%s'", ++ line, pos); ++ return 1; ++ } ++ bss->fils_cache_id_set = 1; ++ } else if (os_strcmp(buf, "fils_realm") == 0) { ++ if (parse_fils_realm(bss, pos) < 0) ++ return 1; ++ } else if (os_strcmp(buf, "fils_dh_group") == 0) { ++ bss->fils_dh_group = atoi(pos); ++ } else if (os_strcmp(buf, "dhcp_server") == 0) { ++ if (hostapd_parse_ip_addr(pos, &bss->dhcp_server)) { ++ wpa_printf(MSG_ERROR, ++ "Line %d: invalid IP address '%s'", ++ line, pos); ++ return 1; ++ } ++ } else if (os_strcmp(buf, "dhcp_rapid_commit_proxy") == 0) { ++ bss->dhcp_rapid_commit_proxy = atoi(pos); ++ } else if (os_strcmp(buf, "fils_hlp_wait_time") == 0) { ++ bss->fils_hlp_wait_time = atoi(pos); ++ } else if (os_strcmp(buf, "dhcp_server_port") == 0) { ++ bss->dhcp_server_port = atoi(pos); ++ } else if (os_strcmp(buf, "dhcp_relay_port") == 0) { ++ bss->dhcp_relay_port = atoi(pos); ++#endif /* CONFIG_FILS */ ++ } else if (os_strcmp(buf, "multicast_to_unicast") == 0) { ++ bss->multicast_to_unicast = atoi(pos); ++ } else if (os_strcmp(buf, "broadcast_deauth") == 0) { ++ bss->broadcast_deauth = atoi(pos); ++#ifdef CONFIG_DPP ++ } else if (os_strcmp(buf, "dpp_connector") == 0) { ++ os_free(bss->dpp_connector); ++ bss->dpp_connector = os_strdup(pos); ++ } else if (os_strcmp(buf, "dpp_netaccesskey") == 0) { ++ if (parse_wpabuf_hex(line, buf, &bss->dpp_netaccesskey, pos)) ++ return 1; ++ } else if (os_strcmp(buf, "dpp_netaccesskey_expiry") == 0) { ++ bss->dpp_netaccesskey_expiry = strtol(pos, NULL, 0); ++ } else if (os_strcmp(buf, "dpp_csign") == 0) { ++ if (parse_wpabuf_hex(line, buf, &bss->dpp_csign, pos)) ++ return 1; ++#endif /* CONFIG_DPP */ ++#ifdef CONFIG_OWE ++ } else if (os_strcmp(buf, "owe_transition_bssid") == 0) { ++ if (hwaddr_aton(pos, bss->owe_transition_bssid)) { ++ wpa_printf(MSG_ERROR, ++ "Line %d: invalid owe_transition_bssid", ++ line); ++ return 1; ++ } ++ } else if (os_strcmp(buf, "owe_transition_ssid") == 0) { ++ size_t slen; ++ char *str = wpa_config_parse_string(pos, &slen); ++ ++ if (!str || slen < 1 || slen > SSID_MAX_LEN) { ++ wpa_printf(MSG_ERROR, "Line %d: invalid SSID '%s'", ++ line, pos); ++ os_free(str); ++ return 1; ++ } ++ os_memcpy(bss->owe_transition_ssid, str, slen); ++ bss->owe_transition_ssid_len = slen; ++ os_free(str); ++ } else if (os_strcmp(buf, "owe_transition_ifname") == 0) { ++ os_strlcpy(bss->owe_transition_ifname, pos, ++ sizeof(bss->owe_transition_ifname)); ++ } else if (os_strcmp(buf, "owe_groups") == 0) { ++ if (hostapd_parse_intlist(&bss->owe_groups, pos)) { ++ wpa_printf(MSG_ERROR, ++ "Line %d: Invalid owe_groups value '%s'", ++ line, pos); ++ return 1; ++ } ++ } else if (os_strcmp(buf, "coloc_intf_reporting") == 0) { ++ bss->coloc_intf_reporting = atoi(pos); ++#endif /* CONFIG_OWE */ ++ } else if (os_strcmp(buf, "multi_ap") == 0) { ++ int val = atoi(pos); ++ ++ if (val < 0 || val > 3) { ++ wpa_printf(MSG_ERROR, "Line %d: Invalid multi_ap '%s'", ++ line, buf); ++ return -1; ++ } ++ ++ bss->multi_ap = val; ++ } else if (os_strcmp(buf, "rssi_reject_assoc_rssi") == 0) { ++ conf->rssi_reject_assoc_rssi = atoi(pos); ++ } else if (os_strcmp(buf, "rssi_reject_assoc_timeout") == 0) { ++ conf->rssi_reject_assoc_timeout = atoi(pos); ++ } else if (os_strcmp(buf, "pbss") == 0) { ++ bss->pbss = atoi(pos); + } else { + wpa_printf(MSG_ERROR, + "Line %d: unknown configuration item '%s'", +@@ -3429,7 +4369,7 @@ + { + struct hostapd_config *conf; + FILE *f; +- char buf[512], *pos; ++ char buf[4096], *pos; + int line = 0; + int errors = 0; + size_t i; +--- contrib/wpa/hostapd/config_file.h.orig ++++ contrib/wpa/hostapd/config_file.h +@@ -13,5 +13,10 @@ + int hostapd_set_iface(struct hostapd_config *conf, + struct hostapd_bss_config *bss, const char *field, + char *value); ++int hostapd_acl_comp(const void *a, const void *b); ++int hostapd_add_acl_maclist(struct mac_acl_entry **acl, int *num, ++ int vlan_id, const u8 *addr); ++void hostapd_remove_acl_mac(struct mac_acl_entry **acl, int *num, ++ const u8 *addr); + + #endif /* CONFIG_FILE_H */ +--- contrib/wpa/hostapd/ctrl_iface.c.orig ++++ contrib/wpa/hostapd/ctrl_iface.c +@@ -1,6 +1,6 @@ + /* + * hostapd / UNIX domain socket -based control interface +- * Copyright (c) 2004-2015, Jouni Malinen ++ * Copyright (c) 2004-2018, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. +@@ -19,10 +19,20 @@ + #include + #include + ++#ifdef CONFIG_CTRL_IFACE_UDP ++#include ++#endif /* CONFIG_CTRL_IFACE_UDP */ ++ + #include "utils/common.h" + #include "utils/eloop.h" ++#include "utils/module_tests.h" + #include "common/version.h" + #include "common/ieee802_11_defs.h" ++#include "common/ctrl_iface_common.h" ++#ifdef CONFIG_DPP ++#include "common/dpp.h" ++#endif /* CONFIG_DPP */ ++#include "common/wpa_ctrl.h" + #include "crypto/tls.h" + #include "drivers/driver.h" + #include "eapol_auth/eapol_auth_sm.h" +@@ -42,6 +52,9 @@ + #include "ap/wnm_ap.h" + #include "ap/wpa_auth.h" + #include "ap/beacon.h" ++#include "ap/neighbor_db.h" ++#include "ap/rrm.h" ++#include "ap/dpp_hostapd.h" + #include "wps/wps_defs.h" + #include "wps/wps.h" + #include "fst/fst_ctrl_iface.h" +@@ -51,15 +64,16 @@ + + #define HOSTAPD_CLI_DUP_VALUE_MAX_LEN 256 + +-struct wpa_ctrl_dst { +- struct wpa_ctrl_dst *next; +- struct sockaddr_un addr; +- socklen_t addrlen; +- int debug_level; +- int errors; +-}; ++#ifdef CONFIG_CTRL_IFACE_UDP ++#define COOKIE_LEN 8 ++static unsigned char cookie[COOKIE_LEN]; ++static unsigned char gcookie[COOKIE_LEN]; ++#define HOSTAPD_CTRL_IFACE_PORT 8877 ++#define HOSTAPD_CTRL_IFACE_PORT_LIMIT 50 ++#define HOSTAPD_GLOBAL_CTRL_IFACE_PORT 8878 ++#define HOSTAPD_GLOBAL_CTRL_IFACE_PORT_LIMIT 50 ++#endif /* CONFIG_CTRL_IFACE_UDP */ + +- + static void hostapd_ctrl_iface_send(struct hostapd_data *hapd, int level, + enum wpa_msg_type type, + const char *buf, size_t len); +@@ -66,81 +80,27 @@ + + + static int hostapd_ctrl_iface_attach(struct hostapd_data *hapd, +- struct sockaddr_un *from, +- socklen_t fromlen) ++ struct sockaddr_storage *from, ++ socklen_t fromlen, const char *input) + { +- struct wpa_ctrl_dst *dst; +- +- dst = os_zalloc(sizeof(*dst)); +- if (dst == NULL) +- return -1; +- os_memcpy(&dst->addr, from, sizeof(struct sockaddr_un)); +- dst->addrlen = fromlen; +- dst->debug_level = MSG_INFO; +- dst->next = hapd->ctrl_dst; +- hapd->ctrl_dst = dst; +- wpa_hexdump(MSG_DEBUG, "CTRL_IFACE monitor attached", +- (u8 *) from->sun_path, +- fromlen - offsetof(struct sockaddr_un, sun_path)); +- return 0; ++ return ctrl_iface_attach(&hapd->ctrl_dst, from, fromlen, input); + } + + + static int hostapd_ctrl_iface_detach(struct hostapd_data *hapd, +- struct sockaddr_un *from, ++ struct sockaddr_storage *from, + socklen_t fromlen) + { +- struct wpa_ctrl_dst *dst, *prev = NULL; +- +- dst = hapd->ctrl_dst; +- while (dst) { +- if (fromlen == dst->addrlen && +- os_memcmp(from->sun_path, dst->addr.sun_path, +- fromlen - offsetof(struct sockaddr_un, sun_path)) +- == 0) { +- wpa_hexdump(MSG_DEBUG, "CTRL_IFACE monitor detached", +- (u8 *) from->sun_path, +- fromlen - +- offsetof(struct sockaddr_un, sun_path)); +- if (prev == NULL) +- hapd->ctrl_dst = dst->next; +- else +- prev->next = dst->next; +- os_free(dst); +- return 0; +- } +- prev = dst; +- dst = dst->next; +- } +- return -1; ++ return ctrl_iface_detach(&hapd->ctrl_dst, from, fromlen); + } + + + static int hostapd_ctrl_iface_level(struct hostapd_data *hapd, +- struct sockaddr_un *from, ++ struct sockaddr_storage *from, + socklen_t fromlen, + char *level) + { +- struct wpa_ctrl_dst *dst; +- +- wpa_printf(MSG_DEBUG, "CTRL_IFACE LEVEL %s", level); +- +- dst = hapd->ctrl_dst; +- while (dst) { +- if (fromlen == dst->addrlen && +- os_memcmp(from->sun_path, dst->addr.sun_path, +- fromlen - offsetof(struct sockaddr_un, sun_path)) +- == 0) { +- wpa_hexdump(MSG_DEBUG, "CTRL_IFACE changed monitor " +- "level", (u8 *) from->sun_path, fromlen - +- offsetof(struct sockaddr_un, sun_path)); +- dst->debug_level = atoi(level); +- return 0; +- } +- dst = dst->next; +- } +- +- return -1; ++ return ctrl_iface_level(&hapd->ctrl_dst, from, fromlen, level); + } + + +@@ -808,7 +768,7 @@ + #endif /* CONFIG_INTERWORKING */ + + +-#ifdef CONFIG_WNM ++#ifdef CONFIG_WNM_AP + + static int hostapd_ctrl_iface_disassoc_imminent(struct hostapd_data *hapd, + const char *cmd) +@@ -883,7 +843,9 @@ + char *url = NULL; + int ret; + u8 nei_rep[1000]; +- u8 *nei_pos = nei_rep; ++ int nei_len; ++ u8 mbo[10]; ++ size_t mbo_len = 0; + + if (hwaddr_aton(cmd, addr)) { + wpa_printf(MSG_DEBUG, "Invalid STA MAC address"); +@@ -921,7 +883,7 @@ + /* TODO: TSF configurable/learnable */ + bss_term_dur[0] = 4; /* Subelement ID */ + bss_term_dur[1] = 10; /* Length */ +- os_memset(bss_term_dur, 2, 8); ++ os_memset(&bss_term_dur[2], 0, 8); + end = os_strchr(pos, ','); + if (end == NULL) { + wpa_printf(MSG_DEBUG, "Invalid bss_term data"); +@@ -931,100 +893,11 @@ + WPA_PUT_LE16(&bss_term_dur[10], atoi(end)); + } + ++ nei_len = ieee802_11_parse_candidate_list(cmd, nei_rep, ++ sizeof(nei_rep)); ++ if (nei_len < 0) ++ return -1; + +- /* +- * BSS Transition Candidate List Entries - Neighbor Report elements +- * neighbor=,,, +- * ,[,] +- */ +- pos = cmd; +- while (pos) { +- u8 *nei_start; +- long int val; +- char *endptr, *tmp; +- +- pos = os_strstr(pos, " neighbor="); +- if (!pos) +- break; +- if (nei_pos + 15 > nei_rep + sizeof(nei_rep)) { +- wpa_printf(MSG_DEBUG, +- "Not enough room for additional neighbor"); +- return -1; +- } +- pos += 10; +- +- nei_start = nei_pos; +- *nei_pos++ = WLAN_EID_NEIGHBOR_REPORT; +- nei_pos++; /* length to be filled in */ +- +- if (hwaddr_aton(pos, nei_pos)) { +- wpa_printf(MSG_DEBUG, "Invalid BSSID"); +- return -1; +- } +- nei_pos += ETH_ALEN; +- pos += 17; +- if (*pos != ',') { +- wpa_printf(MSG_DEBUG, "Missing BSSID Information"); +- return -1; +- } +- pos++; +- +- val = strtol(pos, &endptr, 0); +- WPA_PUT_LE32(nei_pos, val); +- nei_pos += 4; +- if (*endptr != ',') { +- wpa_printf(MSG_DEBUG, "Missing Operating Class"); +- return -1; +- } +- pos = endptr + 1; +- +- *nei_pos++ = atoi(pos); /* Operating Class */ +- pos = os_strchr(pos, ','); +- if (pos == NULL) { +- wpa_printf(MSG_DEBUG, "Missing Channel Number"); +- return -1; +- } +- pos++; +- +- *nei_pos++ = atoi(pos); /* Channel Number */ +- pos = os_strchr(pos, ','); +- if (pos == NULL) { +- wpa_printf(MSG_DEBUG, "Missing PHY Type"); +- return -1; +- } +- pos++; +- +- *nei_pos++ = atoi(pos); /* PHY Type */ +- end = os_strchr(pos, ' '); +- tmp = os_strchr(pos, ','); +- if (tmp && (!end || tmp < end)) { +- /* Optional Subelements (hexdump) */ +- size_t len; +- +- pos = tmp + 1; +- end = os_strchr(pos, ' '); +- if (end) +- len = end - pos; +- else +- len = os_strlen(pos); +- if (nei_pos + len / 2 > nei_rep + sizeof(nei_rep)) { +- wpa_printf(MSG_DEBUG, +- "Not enough room for neighbor subelements"); +- return -1; +- } +- if (len & 0x01 || +- hexstr2bin(pos, nei_pos, len / 2) < 0) { +- wpa_printf(MSG_DEBUG, +- "Invalid neighbor subelement info"); +- return -1; +- } +- nei_pos += len / 2; +- pos = end; +- } +- +- nei_start[1] = nei_pos - nei_start - 2; +- } +- + pos = os_strstr(cmd, " url="); + if (pos) { + size_t len; +@@ -1049,17 +922,115 @@ + if (os_strstr(cmd, " disassoc_imminent=1")) + req_mode |= WNM_BSS_TM_REQ_DISASSOC_IMMINENT; + ++#ifdef CONFIG_MBO ++ pos = os_strstr(cmd, "mbo="); ++ if (pos) { ++ unsigned int mbo_reason, cell_pref, reassoc_delay; ++ u8 *mbo_pos = mbo; ++ ++ ret = sscanf(pos, "mbo=%u:%u:%u", &mbo_reason, ++ &reassoc_delay, &cell_pref); ++ if (ret != 3) { ++ wpa_printf(MSG_DEBUG, ++ "MBO requires three arguments: mbo=::"); ++ ret = -1; ++ goto fail; ++ } ++ ++ if (mbo_reason > MBO_TRANSITION_REASON_PREMIUM_AP) { ++ wpa_printf(MSG_DEBUG, ++ "Invalid MBO transition reason code %u", ++ mbo_reason); ++ ret = -1; ++ goto fail; ++ } ++ ++ /* Valid values for Cellular preference are: 0, 1, 255 */ ++ if (cell_pref != 0 && cell_pref != 1 && cell_pref != 255) { ++ wpa_printf(MSG_DEBUG, ++ "Invalid MBO cellular capability %u", ++ cell_pref); ++ ret = -1; ++ goto fail; ++ } ++ ++ if (reassoc_delay > 65535 || ++ (reassoc_delay && ++ !(req_mode & WNM_BSS_TM_REQ_DISASSOC_IMMINENT))) { ++ wpa_printf(MSG_DEBUG, ++ "MBO: Assoc retry delay is only valid in disassoc imminent mode"); ++ ret = -1; ++ goto fail; ++ } ++ ++ *mbo_pos++ = MBO_ATTR_ID_TRANSITION_REASON; ++ *mbo_pos++ = 1; ++ *mbo_pos++ = mbo_reason; ++ *mbo_pos++ = MBO_ATTR_ID_CELL_DATA_PREF; ++ *mbo_pos++ = 1; ++ *mbo_pos++ = cell_pref; ++ ++ if (reassoc_delay) { ++ *mbo_pos++ = MBO_ATTR_ID_ASSOC_RETRY_DELAY; ++ *mbo_pos++ = 2; ++ WPA_PUT_LE16(mbo_pos, reassoc_delay); ++ mbo_pos += 2; ++ } ++ ++ mbo_len = mbo_pos - mbo; ++ } ++#endif /* CONFIG_MBO */ ++ + ret = wnm_send_bss_tm_req(hapd, sta, req_mode, disassoc_timer, + valid_int, bss_term_dur, url, +- nei_pos > nei_rep ? nei_rep : NULL, +- nei_pos - nei_rep); ++ nei_len ? nei_rep : NULL, nei_len, ++ mbo_len ? mbo : NULL, mbo_len); ++#ifdef CONFIG_MBO ++fail: ++#endif /* CONFIG_MBO */ + os_free(url); + return ret; + } + +-#endif /* CONFIG_WNM */ + ++static int hostapd_ctrl_iface_coloc_intf_req(struct hostapd_data *hapd, ++ const char *cmd) ++{ ++ u8 addr[ETH_ALEN]; ++ struct sta_info *sta; ++ const char *pos; ++ unsigned int auto_report, timeout; + ++ if (hwaddr_aton(cmd, addr)) { ++ wpa_printf(MSG_DEBUG, "Invalid STA MAC address"); ++ return -1; ++ } ++ ++ sta = ap_get_sta(hapd, addr); ++ if (!sta) { ++ wpa_printf(MSG_DEBUG, "Station " MACSTR ++ " not found for Collocated Interference Request", ++ MAC2STR(addr)); ++ return -1; ++ } ++ ++ pos = cmd + 17; ++ if (*pos != ' ') ++ return -1; ++ pos++; ++ auto_report = atoi(pos); ++ pos = os_strchr(pos, ' '); ++ if (!pos) ++ return -1; ++ pos++; ++ timeout = atoi(pos); ++ ++ return wnm_send_coloc_intf_req(hapd, sta, auto_report, timeout); ++} ++ ++#endif /* CONFIG_WNM_AP */ ++ ++ + static int hostapd_ctrl_iface_get_key_mgmt(struct hostapd_data *hapd, + char *buf, size_t buflen) + { +@@ -1083,7 +1054,7 @@ + return pos - buf; + pos += ret; + } +-#ifdef CONFIG_IEEE80211R ++#ifdef CONFIG_IEEE80211R_AP + if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_PSK) { + ret = os_snprintf(pos, end - pos, "FT-PSK "); + if (os_snprintf_error(end - pos, ret)) +@@ -1096,6 +1067,14 @@ + return pos - buf; + pos += ret; + } ++#ifdef CONFIG_SHA384 ++ if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X_SHA384) { ++ ret = os_snprintf(pos, end - pos, "FT-EAP-SHA384 "); ++ if (os_snprintf_error(end - pos, ret)) ++ return pos - buf; ++ pos += ret; ++ } ++#endif /* CONFIG_SHA384 */ + #ifdef CONFIG_SAE + if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_SAE) { + ret = os_snprintf(pos, end - pos, "FT-SAE "); +@@ -1104,7 +1083,21 @@ + pos += ret; + } + #endif /* CONFIG_SAE */ +-#endif /* CONFIG_IEEE80211R */ ++#ifdef CONFIG_FILS ++ if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_FILS_SHA256) { ++ ret = os_snprintf(pos, end - pos, "FT-FILS-SHA256 "); ++ if (os_snprintf_error(end - pos, ret)) ++ return pos - buf; ++ pos += ret; ++ } ++ if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_FILS_SHA384) { ++ ret = os_snprintf(pos, end - pos, "FT-FILS-SHA384 "); ++ if (os_snprintf_error(end - pos, ret)) ++ return pos - buf; ++ pos += ret; ++ } ++#endif /* CONFIG_FILS */ ++#endif /* CONFIG_IEEE80211R_AP */ + #ifdef CONFIG_IEEE80211W + if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_PSK_SHA256) { + ret = os_snprintf(pos, end - pos, "WPA-PSK-SHA256 "); +@@ -1141,7 +1134,39 @@ + return pos - buf; + pos += ret; + } ++#ifdef CONFIG_FILS ++ if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_FILS_SHA256) { ++ ret = os_snprintf(pos, end - pos, "FILS-SHA256 "); ++ if (os_snprintf_error(end - pos, ret)) ++ return pos - buf; ++ pos += ret; ++ } ++ if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_FILS_SHA384) { ++ ret = os_snprintf(pos, end - pos, "FILS-SHA384 "); ++ if (os_snprintf_error(end - pos, ret)) ++ return pos - buf; ++ pos += ret; ++ } ++#endif /* CONFIG_FILS */ + ++#ifdef CONFIG_OWE ++ if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_OWE) { ++ ret = os_snprintf(pos, end - pos, "OWE "); ++ if (os_snprintf_error(end - pos, ret)) ++ return pos - buf; ++ pos += ret; ++ } ++#endif /* CONFIG_OWE */ ++ ++#ifdef CONFIG_DPP ++ if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_DPP) { ++ ret = os_snprintf(pos, end - pos, "DPP "); ++ if (os_snprintf_error(end - pos, ret)) ++ return pos - buf; ++ pos += ret; ++ } ++#endif /* CONFIG_DPP */ ++ + if (pos > buf && *(pos - 1) == ' ') { + *(pos - 1) = '\0'; + pos--; +@@ -1269,6 +1294,42 @@ + } + + ++static void hostapd_disassoc_accept_mac(struct hostapd_data *hapd) ++{ ++ struct sta_info *sta; ++ struct vlan_description vlan_id; ++ ++ if (hapd->conf->macaddr_acl != DENY_UNLESS_ACCEPTED) ++ return; ++ ++ for (sta = hapd->sta_list; sta; sta = sta->next) { ++ if (!hostapd_maclist_found(hapd->conf->accept_mac, ++ hapd->conf->num_accept_mac, ++ sta->addr, &vlan_id) || ++ (vlan_id.notempty && ++ vlan_compare(&vlan_id, sta->vlan_desc))) ++ ap_sta_disconnect(hapd, sta, sta->addr, ++ WLAN_REASON_UNSPECIFIED); ++ } ++} ++ ++ ++static void hostapd_disassoc_deny_mac(struct hostapd_data *hapd) ++{ ++ struct sta_info *sta; ++ struct vlan_description vlan_id; ++ ++ for (sta = hapd->sta_list; sta; sta = sta->next) { ++ if (hostapd_maclist_found(hapd->conf->deny_mac, ++ hapd->conf->num_deny_mac, sta->addr, ++ &vlan_id) && ++ (!vlan_id.notempty || ++ !vlan_compare(&vlan_id, sta->vlan_desc))) ++ ap_sta_disconnect(hapd, sta, sta->addr, ++ WLAN_REASON_UNSPECIFIED); ++ } ++} ++ + static int hostapd_ctrl_iface_set(struct hostapd_data *hapd, char *cmd) + { + char *value; +@@ -1306,51 +1367,67 @@ + wpa_printf(MSG_DEBUG, "WPS: Testing - wps_corrupt_pkhash=%d", + wps_corrupt_pkhash); + #endif /* CONFIG_WPS_TESTING */ +-#ifdef CONFIG_INTERWORKING +- } else if (os_strcasecmp(cmd, "gas_frag_limit") == 0) { +- int val = atoi(value); +- if (val <= 0) +- ret = -1; +- else +- hapd->gas_frag_limit = val; +-#endif /* CONFIG_INTERWORKING */ + #ifdef CONFIG_TESTING_OPTIONS + } else if (os_strcasecmp(cmd, "ext_mgmt_frame_handling") == 0) { + hapd->ext_mgmt_frame_handling = atoi(value); + } else if (os_strcasecmp(cmd, "ext_eapol_frame_io") == 0) { + hapd->ext_eapol_frame_io = atoi(value); ++#ifdef CONFIG_DPP ++ } else if (os_strcasecmp(cmd, "dpp_config_obj_override") == 0) { ++ os_free(hapd->dpp_config_obj_override); ++ hapd->dpp_config_obj_override = os_strdup(value); ++ } else if (os_strcasecmp(cmd, "dpp_discovery_override") == 0) { ++ os_free(hapd->dpp_discovery_override); ++ hapd->dpp_discovery_override = os_strdup(value); ++ } else if (os_strcasecmp(cmd, "dpp_groups_override") == 0) { ++ os_free(hapd->dpp_groups_override); ++ hapd->dpp_groups_override = os_strdup(value); ++ } else if (os_strcasecmp(cmd, ++ "dpp_ignore_netaccesskey_mismatch") == 0) { ++ hapd->dpp_ignore_netaccesskey_mismatch = atoi(value); ++ } else if (os_strcasecmp(cmd, "dpp_test") == 0) { ++ dpp_test = atoi(value); ++#endif /* CONFIG_DPP */ + #endif /* CONFIG_TESTING_OPTIONS */ ++#ifdef CONFIG_MBO ++ } else if (os_strcasecmp(cmd, "mbo_assoc_disallow") == 0) { ++ int val; ++ ++ if (!hapd->conf->mbo_enabled) ++ return -1; ++ ++ val = atoi(value); ++ if (val < 0 || val > 1) ++ return -1; ++ ++ hapd->mbo_assoc_disallow = val; ++ ieee802_11_update_beacons(hapd->iface); ++ ++ /* ++ * TODO: Need to configure drivers that do AP MLME offload with ++ * disallowing station logic. ++ */ ++#endif /* CONFIG_MBO */ ++#ifdef CONFIG_DPP ++ } else if (os_strcasecmp(cmd, "dpp_configurator_params") == 0) { ++ os_free(hapd->dpp_configurator_params); ++ hapd->dpp_configurator_params = os_strdup(value); ++#endif /* CONFIG_DPP */ + } else { +- struct sta_info *sta; +- int vlan_id; +- + ret = hostapd_set_iface(hapd->iconf, hapd->conf, cmd, value); + if (ret) + return ret; + + if (os_strcasecmp(cmd, "deny_mac_file") == 0) { +- for (sta = hapd->sta_list; sta; sta = sta->next) { +- if (hostapd_maclist_found( +- hapd->conf->deny_mac, +- hapd->conf->num_deny_mac, sta->addr, +- &vlan_id) && +- (!vlan_id || vlan_id == sta->vlan_id)) +- ap_sta_disconnect( +- hapd, sta, sta->addr, +- WLAN_REASON_UNSPECIFIED); +- } +- } else if (hapd->conf->macaddr_acl == DENY_UNLESS_ACCEPTED && +- os_strcasecmp(cmd, "accept_mac_file") == 0) { +- for (sta = hapd->sta_list; sta; sta = sta->next) { +- if (!hostapd_maclist_found( +- hapd->conf->accept_mac, +- hapd->conf->num_accept_mac, +- sta->addr, &vlan_id) || +- (vlan_id && vlan_id != sta->vlan_id)) +- ap_sta_disconnect( +- hapd, sta, sta->addr, +- WLAN_REASON_UNSPECIFIED); +- } ++ hostapd_disassoc_deny_mac(hapd); ++ } else if (os_strcasecmp(cmd, "accept_mac_file") == 0) { ++ hostapd_disassoc_accept_mac(hapd); ++ } else if (os_strncmp(cmd, "wme_ac_", 7) == 0 || ++ os_strncmp(cmd, "wmm_ac_", 7) == 0) { ++ hapd->parameter_set_count++; ++ if (ieee802_11_update_beacons(hapd->iface)) ++ wpa_printf(MSG_DEBUG, ++ "Failed to update beacons with WMM parameters"); + } + } + +@@ -1411,6 +1488,63 @@ + } + + ++static int ++hostapd_ctrl_iface_kick_mismatch_psk_sta_iter(struct hostapd_data *hapd, ++ struct sta_info *sta, void *ctx) ++{ ++ struct hostapd_wpa_psk *psk; ++ const u8 *pmk; ++ int pmk_len; ++ int pmk_match; ++ int sta_match; ++ int bss_match; ++ int reason; ++ ++ pmk = wpa_auth_get_pmk(sta->wpa_sm, &pmk_len); ++ ++ for (psk = hapd->conf->ssid.wpa_psk; pmk && psk; psk = psk->next) { ++ pmk_match = PMK_LEN == pmk_len && ++ os_memcmp(psk->psk, pmk, pmk_len) == 0; ++ sta_match = psk->group == 0 && ++ os_memcmp(sta->addr, psk->addr, ETH_ALEN) == 0; ++ bss_match = psk->group == 1; ++ ++ if (pmk_match && (sta_match || bss_match)) ++ return 0; ++ } ++ ++ wpa_printf(MSG_INFO, "STA " MACSTR ++ " PSK/passphrase no longer valid - disconnect", ++ MAC2STR(sta->addr)); ++ reason = WLAN_REASON_PREV_AUTH_NOT_VALID; ++ hostapd_drv_sta_deauth(hapd, sta->addr, reason); ++ ap_sta_deauthenticate(hapd, sta, reason); ++ ++ return 0; ++} ++ ++ ++static int hostapd_ctrl_iface_reload_wpa_psk(struct hostapd_data *hapd) ++{ ++ struct hostapd_bss_config *conf = hapd->conf; ++ int err; ++ ++ hostapd_config_clear_wpa_psk(&conf->ssid.wpa_psk); ++ ++ err = hostapd_setup_wpa_psk(conf); ++ if (err < 0) { ++ wpa_printf(MSG_ERROR, "Reloading WPA-PSK passwords failed: %d", ++ err); ++ return -1; ++ } ++ ++ ap_for_each_sta(hapd, hostapd_ctrl_iface_kick_mismatch_psk_sta_iter, ++ NULL); ++ ++ return 0; ++} ++ ++ + #ifdef CONFIG_TESTING_OPTIONS + + static int hostapd_ctrl_iface_radar(struct hostapd_data *hapd, char *cmd) +@@ -1500,6 +1634,137 @@ + } + + ++static int hostapd_ctrl_iface_mgmt_tx_status_process(struct hostapd_data *hapd, ++ char *cmd) ++{ ++ char *pos, *param; ++ size_t len; ++ u8 *buf; ++ int stype = 0, ok = 0; ++ union wpa_event_data event; ++ ++ if (!hapd->ext_mgmt_frame_handling) ++ return -1; ++ ++ /* stype= ok=<0/1> buf= */ ++ ++ wpa_printf(MSG_DEBUG, "External MGMT TX status process: %s", cmd); ++ ++ pos = cmd; ++ param = os_strstr(pos, "stype="); ++ if (param) { ++ param += 6; ++ stype = atoi(param); ++ } ++ ++ param = os_strstr(pos, " ok="); ++ if (param) { ++ param += 4; ++ ok = atoi(param); ++ } ++ ++ param = os_strstr(pos, " buf="); ++ if (!param) ++ return -1; ++ param += 5; ++ ++ len = os_strlen(param); ++ if (len & 1) ++ return -1; ++ len /= 2; ++ ++ buf = os_malloc(len); ++ if (!buf || hexstr2bin(param, buf, len) < 0) { ++ os_free(buf); ++ return -1; ++ } ++ ++ os_memset(&event, 0, sizeof(event)); ++ event.tx_status.type = WLAN_FC_TYPE_MGMT; ++ event.tx_status.data = buf; ++ event.tx_status.data_len = len; ++ event.tx_status.stype = stype; ++ event.tx_status.ack = ok; ++ hapd->ext_mgmt_frame_handling = 0; ++ wpa_supplicant_event(hapd, EVENT_TX_STATUS, &event); ++ hapd->ext_mgmt_frame_handling = 1; ++ ++ os_free(buf); ++ ++ return 0; ++} ++ ++ ++static int hostapd_ctrl_iface_mgmt_rx_process(struct hostapd_data *hapd, ++ char *cmd) ++{ ++ char *pos, *param; ++ size_t len; ++ u8 *buf; ++ int freq = 0, datarate = 0, ssi_signal = 0; ++ union wpa_event_data event; ++ ++ if (!hapd->ext_mgmt_frame_handling) ++ return -1; ++ ++ /* freq= datarate= ssi_signal= frame= */ ++ ++ wpa_printf(MSG_DEBUG, "External MGMT RX process: %s", cmd); ++ ++ pos = cmd; ++ param = os_strstr(pos, "freq="); ++ if (param) { ++ param += 5; ++ freq = atoi(param); ++ } ++ ++ param = os_strstr(pos, " datarate="); ++ if (param) { ++ param += 10; ++ datarate = atoi(param); ++ } ++ ++ param = os_strstr(pos, " ssi_signal="); ++ if (param) { ++ param += 12; ++ ssi_signal = atoi(param); ++ } ++ ++ param = os_strstr(pos, " frame="); ++ if (param == NULL) ++ return -1; ++ param += 7; ++ ++ len = os_strlen(param); ++ if (len & 1) ++ return -1; ++ len /= 2; ++ ++ buf = os_malloc(len); ++ if (buf == NULL) ++ return -1; ++ ++ if (hexstr2bin(param, buf, len) < 0) { ++ os_free(buf); ++ return -1; ++ } ++ ++ os_memset(&event, 0, sizeof(event)); ++ event.rx_mgmt.freq = freq; ++ event.rx_mgmt.frame = buf; ++ event.rx_mgmt.frame_len = len; ++ event.rx_mgmt.ssi_signal = ssi_signal; ++ event.rx_mgmt.datarate = datarate; ++ hapd->ext_mgmt_frame_handling = 0; ++ wpa_supplicant_event(hapd, EVENT_RX_MGMT, &event); ++ hapd->ext_mgmt_frame_handling = 1; ++ ++ os_free(buf); ++ ++ return 0; ++} ++ ++ + static int hostapd_ctrl_iface_eapol_rx(struct hostapd_data *hapd, char *cmd) + { + char *pos; +@@ -1557,8 +1822,8 @@ + #define HWSIM_PACKETLEN 1500 + #define HWSIM_IP_LEN (HWSIM_PACKETLEN - sizeof(struct ether_header)) + +-void hostapd_data_test_rx(void *ctx, const u8 *src_addr, const u8 *buf, +- size_t len) ++static void hostapd_data_test_rx(void *ctx, const u8 *src_addr, const u8 *buf, ++ size_t len) + { + struct hostapd_data *hapd = ctx; + const struct ether_header *eth; +@@ -1745,8 +2010,6 @@ + static int hostapd_ctrl_test_alloc_fail(struct hostapd_data *hapd, char *cmd) + { + #ifdef WPA_TRACE_BFD +- extern char wpa_trace_fail_func[256]; +- extern unsigned int wpa_trace_fail_after; + char *pos; + + wpa_trace_fail_after = atoi(cmd); +@@ -1770,9 +2033,6 @@ + char *buf, size_t buflen) + { + #ifdef WPA_TRACE_BFD +- extern char wpa_trace_fail_func[256]; +- extern unsigned int wpa_trace_fail_after; +- + return os_snprintf(buf, buflen, "%u:%s", wpa_trace_fail_after, + wpa_trace_fail_func); + #else /* WPA_TRACE_BFD */ +@@ -1784,8 +2044,6 @@ + static int hostapd_ctrl_test_fail(struct hostapd_data *hapd, char *cmd) + { + #ifdef WPA_TRACE_BFD +- extern char wpa_trace_test_fail_func[256]; +- extern unsigned int wpa_trace_test_fail_after; + char *pos; + + wpa_trace_test_fail_after = atoi(cmd); +@@ -1809,9 +2067,6 @@ + char *buf, size_t buflen) + { + #ifdef WPA_TRACE_BFD +- extern char wpa_trace_test_fail_func[256]; +- extern unsigned int wpa_trace_test_fail_after; +- + return os_snprintf(buf, buflen, "%u:%s", wpa_trace_test_fail_after, + wpa_trace_test_fail_func); + #else /* WPA_TRACE_BFD */ +@@ -1819,6 +2074,245 @@ + #endif /* WPA_TRACE_BFD */ + } + ++ ++static int hostapd_ctrl_reset_pn(struct hostapd_data *hapd, const char *cmd) ++{ ++ struct sta_info *sta; ++ u8 addr[ETH_ALEN]; ++ u8 zero[WPA_TK_MAX_LEN]; ++ ++ os_memset(zero, 0, sizeof(zero)); ++ ++ if (hwaddr_aton(cmd, addr)) ++ return -1; ++ ++#ifdef CONFIG_IEEE80211W ++ if (is_broadcast_ether_addr(addr) && os_strstr(cmd, "IGTK")) { ++ if (hapd->last_igtk_alg == WPA_ALG_NONE) ++ return -1; ++ ++ wpa_printf(MSG_INFO, "TESTING: Reset IPN for IGTK"); ++ ++ /* First, use a zero key to avoid any possible duplicate key ++ * avoidance in the driver. */ ++ if (hostapd_drv_set_key(hapd->conf->iface, hapd, ++ hapd->last_igtk_alg, ++ broadcast_ether_addr, ++ hapd->last_igtk_key_idx, 1, NULL, 0, ++ zero, hapd->last_igtk_len) < 0) ++ return -1; ++ ++ /* Set the previously configured key to reset its TSC */ ++ return hostapd_drv_set_key(hapd->conf->iface, hapd, ++ hapd->last_igtk_alg, ++ broadcast_ether_addr, ++ hapd->last_igtk_key_idx, 1, NULL, 0, ++ hapd->last_igtk, ++ hapd->last_igtk_len); ++ } ++#endif /* CONFIG_IEEE80211W */ ++ ++ if (is_broadcast_ether_addr(addr)) { ++ if (hapd->last_gtk_alg == WPA_ALG_NONE) ++ return -1; ++ ++ wpa_printf(MSG_INFO, "TESTING: Reset PN for GTK"); ++ ++ /* First, use a zero key to avoid any possible duplicate key ++ * avoidance in the driver. */ ++ if (hostapd_drv_set_key(hapd->conf->iface, hapd, ++ hapd->last_gtk_alg, ++ broadcast_ether_addr, ++ hapd->last_gtk_key_idx, 1, NULL, 0, ++ zero, hapd->last_gtk_len) < 0) ++ return -1; ++ ++ /* Set the previously configured key to reset its TSC */ ++ return hostapd_drv_set_key(hapd->conf->iface, hapd, ++ hapd->last_gtk_alg, ++ broadcast_ether_addr, ++ hapd->last_gtk_key_idx, 1, NULL, 0, ++ hapd->last_gtk, hapd->last_gtk_len); ++ } ++ ++ sta = ap_get_sta(hapd, addr); ++ if (!sta) ++ return -1; ++ ++ if (sta->last_tk_alg == WPA_ALG_NONE) ++ return -1; ++ ++ wpa_printf(MSG_INFO, "TESTING: Reset PN for " MACSTR, ++ MAC2STR(sta->addr)); ++ ++ /* First, use a zero key to avoid any possible duplicate key avoidance ++ * in the driver. */ ++ if (hostapd_drv_set_key(hapd->conf->iface, hapd, sta->last_tk_alg, ++ sta->addr, sta->last_tk_key_idx, 1, NULL, 0, ++ zero, sta->last_tk_len) < 0) ++ return -1; ++ ++ /* Set the previously configured key to reset its TSC/RSC */ ++ return hostapd_drv_set_key(hapd->conf->iface, hapd, sta->last_tk_alg, ++ sta->addr, sta->last_tk_key_idx, 1, NULL, 0, ++ sta->last_tk, sta->last_tk_len); ++} ++ ++ ++static int hostapd_ctrl_set_key(struct hostapd_data *hapd, const char *cmd) ++{ ++ u8 addr[ETH_ALEN]; ++ const char *pos = cmd; ++ enum wpa_alg alg; ++ int idx, set_tx; ++ u8 seq[6], key[WPA_TK_MAX_LEN]; ++ size_t key_len; ++ ++ /* parameters: alg addr idx set_tx seq key */ ++ ++ alg = atoi(pos); ++ pos = os_strchr(pos, ' '); ++ if (!pos) ++ return -1; ++ pos++; ++ if (hwaddr_aton(pos, addr)) ++ return -1; ++ pos += 17; ++ if (*pos != ' ') ++ return -1; ++ pos++; ++ idx = atoi(pos); ++ pos = os_strchr(pos, ' '); ++ if (!pos) ++ return -1; ++ pos++; ++ set_tx = atoi(pos); ++ pos = os_strchr(pos, ' '); ++ if (!pos) ++ return -1; ++ pos++; ++ if (hexstr2bin(pos, seq, sizeof(seq)) < 0) ++ return -1; ++ pos += 2 * 6; ++ if (*pos != ' ') ++ return -1; ++ pos++; ++ key_len = os_strlen(pos) / 2; ++ if (hexstr2bin(pos, key, key_len) < 0) ++ return -1; ++ ++ wpa_printf(MSG_INFO, "TESTING: Set key"); ++ return hostapd_drv_set_key(hapd->conf->iface, hapd, alg, addr, idx, ++ set_tx, seq, 6, key, key_len); ++} ++ ++ ++static void restore_tk(void *ctx1, void *ctx2) ++{ ++ struct hostapd_data *hapd = ctx1; ++ struct sta_info *sta = ctx2; ++ ++ wpa_printf(MSG_INFO, "TESTING: Restore TK for " MACSTR, ++ MAC2STR(sta->addr)); ++ /* This does not really restore the TSC properly, so this will result ++ * in replay protection issues for now since there is no clean way of ++ * preventing encryption of a single EAPOL frame. */ ++ hostapd_drv_set_key(hapd->conf->iface, hapd, sta->last_tk_alg, ++ sta->addr, sta->last_tk_key_idx, 1, NULL, 0, ++ sta->last_tk, sta->last_tk_len); ++} ++ ++ ++static int hostapd_ctrl_resend_m1(struct hostapd_data *hapd, const char *cmd) ++{ ++ struct sta_info *sta; ++ u8 addr[ETH_ALEN]; ++ int plain = os_strstr(cmd, "plaintext") != NULL; ++ ++ if (hwaddr_aton(cmd, addr)) ++ return -1; ++ ++ sta = ap_get_sta(hapd, addr); ++ if (!sta || !sta->wpa_sm) ++ return -1; ++ ++ if (plain && sta->last_tk_alg == WPA_ALG_NONE) ++ plain = 0; /* no need for special processing */ ++ if (plain) { ++ wpa_printf(MSG_INFO, "TESTING: Clear TK for " MACSTR, ++ MAC2STR(sta->addr)); ++ hostapd_drv_set_key(hapd->conf->iface, hapd, WPA_ALG_NONE, ++ sta->addr, sta->last_tk_key_idx, 0, NULL, 0, ++ NULL, 0); ++ } ++ ++ wpa_printf(MSG_INFO, "TESTING: Send M1 to " MACSTR, MAC2STR(sta->addr)); ++ return wpa_auth_resend_m1(sta->wpa_sm, ++ os_strstr(cmd, "change-anonce") != NULL, ++ plain ? restore_tk : NULL, hapd, sta); ++} ++ ++ ++static int hostapd_ctrl_resend_m3(struct hostapd_data *hapd, const char *cmd) ++{ ++ struct sta_info *sta; ++ u8 addr[ETH_ALEN]; ++ int plain = os_strstr(cmd, "plaintext") != NULL; ++ ++ if (hwaddr_aton(cmd, addr)) ++ return -1; ++ ++ sta = ap_get_sta(hapd, addr); ++ if (!sta || !sta->wpa_sm) ++ return -1; ++ ++ if (plain && sta->last_tk_alg == WPA_ALG_NONE) ++ plain = 0; /* no need for special processing */ ++ if (plain) { ++ wpa_printf(MSG_INFO, "TESTING: Clear TK for " MACSTR, ++ MAC2STR(sta->addr)); ++ hostapd_drv_set_key(hapd->conf->iface, hapd, WPA_ALG_NONE, ++ sta->addr, sta->last_tk_key_idx, 0, NULL, 0, ++ NULL, 0); ++ } ++ ++ wpa_printf(MSG_INFO, "TESTING: Send M3 to " MACSTR, MAC2STR(sta->addr)); ++ return wpa_auth_resend_m3(sta->wpa_sm, ++ plain ? restore_tk : NULL, hapd, sta); ++} ++ ++ ++static int hostapd_ctrl_resend_group_m1(struct hostapd_data *hapd, ++ const char *cmd) ++{ ++ struct sta_info *sta; ++ u8 addr[ETH_ALEN]; ++ int plain = os_strstr(cmd, "plaintext") != NULL; ++ ++ if (hwaddr_aton(cmd, addr)) ++ return -1; ++ ++ sta = ap_get_sta(hapd, addr); ++ if (!sta || !sta->wpa_sm) ++ return -1; ++ ++ if (plain && sta->last_tk_alg == WPA_ALG_NONE) ++ plain = 0; /* no need for special processing */ ++ if (plain) { ++ wpa_printf(MSG_INFO, "TESTING: Clear TK for " MACSTR, ++ MAC2STR(sta->addr)); ++ hostapd_drv_set_key(hapd->conf->iface, hapd, WPA_ALG_NONE, ++ sta->addr, sta->last_tk_key_idx, 0, NULL, 0, ++ NULL, 0); ++ } ++ ++ wpa_printf(MSG_INFO, ++ "TESTING: Send group M1 for the same GTK and zero RSC to " ++ MACSTR, MAC2STR(sta->addr)); ++ return wpa_auth_resend_group_m1(sta->wpa_sm, ++ plain ? restore_tk : NULL, hapd, sta); ++} ++ + #endif /* CONFIG_TESTING_OPTIONS */ + + +@@ -1835,6 +2329,11 @@ + return ret; + + for (i = 0; i < iface->num_bss; i++) { ++ ++ /* Save CHAN_SWITCH VHT config */ ++ hostapd_chan_switch_vht_config( ++ iface->bss[i], settings.freq_params.vht_enabled); ++ + ret = hostapd_switch_channel(iface->bss[i], &settings); + if (ret) { + /* FIX: What do we do if CSA fails in the middle of +@@ -1875,13 +2374,13 @@ + + /* cmd: [] */ + vendor_id = strtoul(cmd, &pos, 16); +- if (!isblank(*pos)) ++ if (!isblank((unsigned char) *pos)) + return -EINVAL; + + subcmd = strtoul(pos, &pos, 10); + + if (*pos != '\0') { +- if (!isblank(*pos++)) ++ if (!isblank((unsigned char) *pos++)) + return -EINVAL; + data_len = os_strlen(pos); + } +@@ -2016,6 +2515,9 @@ + struct hostapd_sta_info *info; + struct os_reltime now; + ++ if (!iface->num_sta_seen) ++ return 0; ++ + sta_track_expire(iface, 0); + + pos = buf; +@@ -2028,8 +2530,9 @@ + int ret; + + os_reltime_sub(&now, &info->last_seen, &age); +- ret = os_snprintf(pos, end - pos, MACSTR " %u\n", +- MAC2STR(info->addr), (unsigned int) age.sec); ++ ret = os_snprintf(pos, end - pos, MACSTR " %u %d\n", ++ MAC2STR(info->addr), (unsigned int) age.sec, ++ info->ssi_signal); + if (os_snprintf_error(end - pos, ret)) + break; + pos += ret; +@@ -2040,10 +2543,378 @@ + #endif /* NEED_AP_MLME */ + + ++static int hostapd_ctrl_iface_req_lci(struct hostapd_data *hapd, ++ const char *cmd) ++{ ++ u8 addr[ETH_ALEN]; ++ ++ if (hwaddr_aton(cmd, addr)) { ++ wpa_printf(MSG_INFO, "CTRL: REQ_LCI: Invalid MAC address"); ++ return -1; ++ } ++ ++ return hostapd_send_lci_req(hapd, addr); ++} ++ ++ ++static int hostapd_ctrl_iface_req_range(struct hostapd_data *hapd, char *cmd) ++{ ++ u8 addr[ETH_ALEN]; ++ char *token, *context = NULL; ++ int random_interval, min_ap; ++ u8 responders[ETH_ALEN * RRM_RANGE_REQ_MAX_RESPONDERS]; ++ unsigned int n_responders; ++ ++ token = str_token(cmd, " ", &context); ++ if (!token || hwaddr_aton(token, addr)) { ++ wpa_printf(MSG_INFO, ++ "CTRL: REQ_RANGE - Bad destination address"); ++ return -1; ++ } ++ ++ token = str_token(cmd, " ", &context); ++ if (!token) ++ return -1; ++ ++ random_interval = atoi(token); ++ if (random_interval < 0 || random_interval > 0xffff) ++ return -1; ++ ++ token = str_token(cmd, " ", &context); ++ if (!token) ++ return -1; ++ ++ min_ap = atoi(token); ++ if (min_ap <= 0 || min_ap > WLAN_RRM_RANGE_REQ_MAX_MIN_AP) ++ return -1; ++ ++ n_responders = 0; ++ while ((token = str_token(cmd, " ", &context))) { ++ if (n_responders == RRM_RANGE_REQ_MAX_RESPONDERS) { ++ wpa_printf(MSG_INFO, ++ "CTRL: REQ_RANGE: Too many responders"); ++ return -1; ++ } ++ ++ if (hwaddr_aton(token, responders + n_responders * ETH_ALEN)) { ++ wpa_printf(MSG_INFO, ++ "CTRL: REQ_RANGE: Bad responder address"); ++ return -1; ++ } ++ ++ n_responders++; ++ } ++ ++ if (!n_responders) { ++ wpa_printf(MSG_INFO, ++ "CTRL: REQ_RANGE - No FTM responder address"); ++ return -1; ++ } ++ ++ return hostapd_send_range_req(hapd, addr, random_interval, min_ap, ++ responders, n_responders); ++} ++ ++ ++static int hostapd_ctrl_iface_req_beacon(struct hostapd_data *hapd, ++ const char *cmd, char *reply, ++ size_t reply_size) ++{ ++ u8 addr[ETH_ALEN]; ++ const char *pos; ++ struct wpabuf *req; ++ int ret; ++ u8 req_mode = 0; ++ ++ if (hwaddr_aton(cmd, addr)) ++ return -1; ++ pos = os_strchr(cmd, ' '); ++ if (!pos) ++ return -1; ++ pos++; ++ if (os_strncmp(pos, "req_mode=", 9) == 0) { ++ int val = hex2byte(pos + 9); ++ ++ if (val < 0) ++ return -1; ++ req_mode = val; ++ pos += 11; ++ pos = os_strchr(pos, ' '); ++ if (!pos) ++ return -1; ++ pos++; ++ } ++ req = wpabuf_parse_bin(pos); ++ if (!req) ++ return -1; ++ ++ ret = hostapd_send_beacon_req(hapd, addr, req_mode, req); ++ wpabuf_free(req); ++ if (ret >= 0) ++ ret = os_snprintf(reply, reply_size, "%d", ret); ++ return ret; ++} ++ ++ ++static int hostapd_ctrl_iface_set_neighbor(struct hostapd_data *hapd, char *buf) ++{ ++ struct wpa_ssid_value ssid; ++ u8 bssid[ETH_ALEN]; ++ struct wpabuf *nr, *lci = NULL, *civic = NULL; ++ int stationary = 0; ++ char *tmp; ++ int ret; ++ ++ if (!(hapd->conf->radio_measurements[0] & ++ WLAN_RRM_CAPS_NEIGHBOR_REPORT)) { ++ wpa_printf(MSG_ERROR, ++ "CTRL: SET_NEIGHBOR: Neighbor report is not enabled"); ++ return -1; ++ } ++ ++ if (hwaddr_aton(buf, bssid)) { ++ wpa_printf(MSG_ERROR, "CTRL: SET_NEIGHBOR: Bad BSSID"); ++ return -1; ++ } ++ ++ tmp = os_strstr(buf, "ssid="); ++ if (!tmp || ssid_parse(tmp + 5, &ssid)) { ++ wpa_printf(MSG_ERROR, ++ "CTRL: SET_NEIGHBOR: Bad or missing SSID"); ++ return -1; ++ } ++ buf = os_strchr(tmp + 6, tmp[5] == '"' ? '"' : ' '); ++ if (!buf) ++ return -1; ++ ++ tmp = os_strstr(buf, "nr="); ++ if (!tmp) { ++ wpa_printf(MSG_ERROR, ++ "CTRL: SET_NEIGHBOR: Missing Neighbor Report element"); ++ return -1; ++ } ++ ++ buf = os_strchr(tmp, ' '); ++ if (buf) ++ *buf++ = '\0'; ++ ++ nr = wpabuf_parse_bin(tmp + 3); ++ if (!nr) { ++ wpa_printf(MSG_ERROR, ++ "CTRL: SET_NEIGHBOR: Bad Neighbor Report element"); ++ return -1; ++ } ++ ++ if (!buf) ++ goto set; ++ ++ tmp = os_strstr(buf, "lci="); ++ if (tmp) { ++ buf = os_strchr(tmp, ' '); ++ if (buf) ++ *buf++ = '\0'; ++ lci = wpabuf_parse_bin(tmp + 4); ++ if (!lci) { ++ wpa_printf(MSG_ERROR, ++ "CTRL: SET_NEIGHBOR: Bad LCI subelement"); ++ wpabuf_free(nr); ++ return -1; ++ } ++ } ++ ++ if (!buf) ++ goto set; ++ ++ tmp = os_strstr(buf, "civic="); ++ if (tmp) { ++ buf = os_strchr(tmp, ' '); ++ if (buf) ++ *buf++ = '\0'; ++ civic = wpabuf_parse_bin(tmp + 6); ++ if (!civic) { ++ wpa_printf(MSG_ERROR, ++ "CTRL: SET_NEIGHBOR: Bad civic subelement"); ++ wpabuf_free(nr); ++ wpabuf_free(lci); ++ return -1; ++ } ++ } ++ ++ if (!buf) ++ goto set; ++ ++ if (os_strstr(buf, "stat")) ++ stationary = 1; ++ ++set: ++ ret = hostapd_neighbor_set(hapd, bssid, &ssid, nr, lci, civic, ++ stationary); ++ ++ wpabuf_free(nr); ++ wpabuf_free(lci); ++ wpabuf_free(civic); ++ ++ return ret; ++} ++ ++ ++static int hostapd_ctrl_iface_remove_neighbor(struct hostapd_data *hapd, ++ char *buf) ++{ ++ struct wpa_ssid_value ssid; ++ u8 bssid[ETH_ALEN]; ++ char *tmp; ++ ++ if (hwaddr_aton(buf, bssid)) { ++ wpa_printf(MSG_ERROR, "CTRL: REMOVE_NEIGHBOR: Bad BSSID"); ++ return -1; ++ } ++ ++ tmp = os_strstr(buf, "ssid="); ++ if (!tmp || ssid_parse(tmp + 5, &ssid)) { ++ wpa_printf(MSG_ERROR, ++ "CTRL: REMOVE_NEIGHBORr: Bad or missing SSID"); ++ return -1; ++ } ++ ++ return hostapd_neighbor_remove(hapd, bssid, &ssid); ++} ++ ++ ++static int hostapd_ctrl_driver_flags(struct hostapd_iface *iface, char *buf, ++ size_t buflen) ++{ ++ int ret, i; ++ char *pos, *end; ++ ++ ret = os_snprintf(buf, buflen, "%016llX:\n", ++ (long long unsigned) iface->drv_flags); ++ if (os_snprintf_error(buflen, ret)) ++ return -1; ++ ++ pos = buf + ret; ++ end = buf + buflen; ++ ++ for (i = 0; i < 64; i++) { ++ if (iface->drv_flags & (1LLU << i)) { ++ ret = os_snprintf(pos, end - pos, "%s\n", ++ driver_flag_to_string(1LLU << i)); ++ if (os_snprintf_error(end - pos, ret)) ++ return -1; ++ pos += ret; ++ } ++ } ++ ++ return pos - buf; ++} ++ ++ ++static int hostapd_ctrl_iface_acl_del_mac(struct mac_acl_entry **acl, int *num, ++ const char *txtaddr) ++{ ++ u8 addr[ETH_ALEN]; ++ struct vlan_description vlan_id; ++ ++ if (!(*num)) ++ return 0; ++ ++ if (hwaddr_aton(txtaddr, addr)) ++ return -1; ++ ++ if (hostapd_maclist_found(*acl, *num, addr, &vlan_id)) ++ hostapd_remove_acl_mac(acl, num, addr); ++ ++ return 0; ++} ++ ++ ++static void hostapd_ctrl_iface_acl_clear_list(struct mac_acl_entry **acl, ++ int *num) ++{ ++ while (*num) ++ hostapd_remove_acl_mac(acl, num, (*acl)[0].addr); ++} ++ ++ ++static int hostapd_ctrl_iface_acl_show_mac(struct mac_acl_entry *acl, int num, ++ char *buf, size_t buflen) ++{ ++ int i = 0, len = 0, ret = 0; ++ ++ if (!acl) ++ return 0; ++ ++ while (i < num) { ++ ret = os_snprintf(buf + len, buflen - len, ++ MACSTR " VLAN_ID=%d\n", ++ MAC2STR(acl[i].addr), ++ acl[i].vlan_id.untagged); ++ if (ret < 0 || (size_t) ret >= buflen - len) ++ return len; ++ i++; ++ len += ret; ++ } ++ return len; ++} ++ ++ ++static int hostapd_ctrl_iface_acl_add_mac(struct mac_acl_entry **acl, int *num, ++ const char *cmd) ++{ ++ u8 addr[ETH_ALEN]; ++ struct vlan_description vlan_id; ++ int ret = 0, vlanid = 0; ++ const char *pos; ++ ++ if (hwaddr_aton(cmd, addr)) ++ return -1; ++ ++ pos = os_strstr(cmd, "VLAN_ID="); ++ if (pos) ++ vlanid = atoi(pos + 8); ++ ++ if (!hostapd_maclist_found(*acl, *num, addr, &vlan_id)) { ++ ret = hostapd_add_acl_maclist(acl, num, vlanid, addr); ++ if (ret != -1 && *acl) ++ qsort(*acl, *num, sizeof(**acl), hostapd_acl_comp); ++ } ++ ++ return ret < 0 ? -1 : 0; ++} ++ ++ ++static int hostapd_ctrl_iface_get_capability(struct hostapd_data *hapd, ++ const char *field, char *buf, ++ size_t buflen) ++{ ++ wpa_printf(MSG_DEBUG, "CTRL_IFACE: GET_CAPABILITY '%s'", field); ++ ++#ifdef CONFIG_DPP ++ if (os_strcmp(field, "dpp") == 0) { ++ int res; ++ ++#ifdef CONFIG_DPP2 ++ res = os_snprintf(buf, buflen, "DPP=2"); ++#else /* CONFIG_DPP2 */ ++ res = os_snprintf(buf, buflen, "DPP=1"); ++#endif /* CONFIG_DPP2 */ ++ if (os_snprintf_error(buflen, res)) ++ return -1; ++ return res; ++ } ++#endif /* CONFIG_DPP */ ++ ++ wpa_printf(MSG_DEBUG, "CTRL_IFACE: Unknown GET_CAPABILITY field '%s'", ++ field); ++ ++ return -1; ++} ++ ++ + static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd, + char *buf, char *reply, + int reply_size, +- struct sockaddr_un *from, ++ struct sockaddr_storage *from, + socklen_t fromlen) + { + int reply_len, res; +@@ -2057,6 +2928,8 @@ + } else if (os_strncmp(buf, "RELOG", 5) == 0) { + if (wpa_debug_reopen_file() < 0) + reply_len = -1; ++ } else if (os_strncmp(buf, "NOTE ", 5) == 0) { ++ wpa_printf(MSG_INFO, "NOTE: %s", buf + 5); + } else if (os_strcmp(buf, "STATUS") == 0) { + reply_len = hostapd_ctrl_iface_status(hapd, reply, + reply_size); +@@ -2104,8 +2977,11 @@ + reply_len = hostapd_ctrl_iface_sta_next(hapd, buf + 9, reply, + reply_size); + } else if (os_strcmp(buf, "ATTACH") == 0) { +- if (hostapd_ctrl_iface_attach(hapd, from, fromlen)) ++ if (hostapd_ctrl_iface_attach(hapd, from, fromlen, NULL)) + reply_len = -1; ++ } else if (os_strncmp(buf, "ATTACH ", 7) == 0) { ++ if (hostapd_ctrl_iface_attach(hapd, from, fromlen, buf + 7)) ++ reply_len = -1; + } else if (os_strcmp(buf, "DETACH") == 0) { + if (hostapd_ctrl_iface_detach(hapd, from, fromlen)) + reply_len = -1; +@@ -2122,6 +2998,14 @@ + } else if (os_strncmp(buf, "DISASSOCIATE ", 13) == 0) { + if (hostapd_ctrl_iface_disassociate(hapd, buf + 13)) + reply_len = -1; ++#ifdef CONFIG_TAXONOMY ++ } else if (os_strncmp(buf, "SIGNATURE ", 10) == 0) { ++ reply_len = hostapd_ctrl_iface_signature(hapd, buf + 10, ++ reply, reply_size); ++#endif /* CONFIG_TAXONOMY */ ++ } else if (os_strncmp(buf, "POLL_STA ", 9) == 0) { ++ if (hostapd_ctrl_iface_poll_sta(hapd, buf + 9)) ++ reply_len = -1; + } else if (os_strcmp(buf, "STOP_AP") == 0) { + if (hostapd_ctrl_iface_stop_ap(hapd)) + reply_len = -1; +@@ -2188,7 +3072,7 @@ + if (hostapd_ctrl_iface_hs20_deauth_req(hapd, buf + 16)) + reply_len = -1; + #endif /* CONFIG_HS20 */ +-#ifdef CONFIG_WNM ++#ifdef CONFIG_WNM_AP + } else if (os_strncmp(buf, "DISASSOC_IMMINENT ", 18) == 0) { + if (hostapd_ctrl_iface_disassoc_imminent(hapd, buf + 18)) + reply_len = -1; +@@ -2198,7 +3082,10 @@ + } else if (os_strncmp(buf, "BSS_TM_REQ ", 11) == 0) { + if (hostapd_ctrl_iface_bss_tm_req(hapd, buf + 11)) + reply_len = -1; +-#endif /* CONFIG_WNM */ ++ } else if (os_strncmp(buf, "COLOC_INTF_REQ ", 15) == 0) { ++ if (hostapd_ctrl_iface_coloc_intf_req(hapd, buf + 15)) ++ reply_len = -1; ++#endif /* CONFIG_WNM_AP */ + } else if (os_strcmp(buf, "GET_CONFIG") == 0) { + reply_len = hostapd_ctrl_iface_get_config(hapd, reply, + reply_size); +@@ -2211,6 +3098,9 @@ + } else if (os_strncmp(buf, "ENABLE", 6) == 0) { + if (hostapd_ctrl_iface_enable(hapd->iface)) + reply_len = -1; ++ } else if (os_strcmp(buf, "RELOAD_WPA_PSK") == 0) { ++ if (hostapd_ctrl_iface_reload_wpa_psk(hapd)) ++ reply_len = -1; + } else if (os_strncmp(buf, "RELOAD", 6) == 0) { + if (hostapd_ctrl_iface_reload(hapd->iface)) + reply_len = -1; +@@ -2227,6 +3117,13 @@ + } else if (os_strncmp(buf, "MGMT_TX ", 8) == 0) { + if (hostapd_ctrl_iface_mgmt_tx(hapd, buf + 8)) + reply_len = -1; ++ } else if (os_strncmp(buf, "MGMT_TX_STATUS_PROCESS ", 23) == 0) { ++ if (hostapd_ctrl_iface_mgmt_tx_status_process(hapd, ++ buf + 23) < 0) ++ reply_len = -1; ++ } else if (os_strncmp(buf, "MGMT_RX_PROCESS ", 16) == 0) { ++ if (hostapd_ctrl_iface_mgmt_rx_process(hapd, buf + 16) < 0) ++ reply_len = -1; + } else if (os_strncmp(buf, "EAPOL_RX ", 9) == 0) { + if (hostapd_ctrl_iface_eapol_rx(hapd, buf + 9) < 0) + reply_len = -1; +@@ -2250,6 +3147,24 @@ + reply_len = -1; + } else if (os_strcmp(buf, "GET_FAIL") == 0) { + reply_len = hostapd_ctrl_get_fail(hapd, reply, reply_size); ++ } else if (os_strncmp(buf, "RESET_PN ", 9) == 0) { ++ if (hostapd_ctrl_reset_pn(hapd, buf + 9) < 0) ++ reply_len = -1; ++ } else if (os_strncmp(buf, "SET_KEY ", 8) == 0) { ++ if (hostapd_ctrl_set_key(hapd, buf + 8) < 0) ++ reply_len = -1; ++ } else if (os_strncmp(buf, "RESEND_M1 ", 10) == 0) { ++ if (hostapd_ctrl_resend_m1(hapd, buf + 10) < 0) ++ reply_len = -1; ++ } else if (os_strncmp(buf, "RESEND_M3 ", 10) == 0) { ++ if (hostapd_ctrl_resend_m3(hapd, buf + 10) < 0) ++ reply_len = -1; ++ } else if (os_strncmp(buf, "RESEND_GROUP_M1 ", 16) == 0) { ++ if (hostapd_ctrl_resend_group_m1(hapd, buf + 16) < 0) ++ reply_len = -1; ++ } else if (os_strcmp(buf, "REKEY_GTK") == 0) { ++ if (wpa_auth_rekey_gtk(hapd->wpa_auth) < 0) ++ reply_len = -1; + #endif /* CONFIG_TESTING_OPTIONS */ + } else if (os_strncmp(buf, "CHAN_SWITCH ", 12) == 0) { + if (hostapd_ctrl_iface_chan_switch(hapd->iface, buf + 12)) +@@ -2276,6 +3191,165 @@ + reply_len = hostapd_ctrl_iface_track_sta_list( + hapd, reply, reply_size); + #endif /* NEED_AP_MLME */ ++ } else if (os_strcmp(buf, "PMKSA") == 0) { ++ reply_len = hostapd_ctrl_iface_pmksa_list(hapd, reply, ++ reply_size); ++ } else if (os_strcmp(buf, "PMKSA_FLUSH") == 0) { ++ hostapd_ctrl_iface_pmksa_flush(hapd); ++ } else if (os_strncmp(buf, "PMKSA_ADD ", 10) == 0) { ++ if (hostapd_ctrl_iface_pmksa_add(hapd, buf + 10) < 0) ++ reply_len = -1; ++ } else if (os_strncmp(buf, "SET_NEIGHBOR ", 13) == 0) { ++ if (hostapd_ctrl_iface_set_neighbor(hapd, buf + 13)) ++ reply_len = -1; ++ } else if (os_strncmp(buf, "REMOVE_NEIGHBOR ", 16) == 0) { ++ if (hostapd_ctrl_iface_remove_neighbor(hapd, buf + 16)) ++ reply_len = -1; ++ } else if (os_strncmp(buf, "REQ_LCI ", 8) == 0) { ++ if (hostapd_ctrl_iface_req_lci(hapd, buf + 8)) ++ reply_len = -1; ++ } else if (os_strncmp(buf, "REQ_RANGE ", 10) == 0) { ++ if (hostapd_ctrl_iface_req_range(hapd, buf + 10)) ++ reply_len = -1; ++ } else if (os_strncmp(buf, "REQ_BEACON ", 11) == 0) { ++ reply_len = hostapd_ctrl_iface_req_beacon(hapd, buf + 11, ++ reply, reply_size); ++ } else if (os_strcmp(buf, "DRIVER_FLAGS") == 0) { ++ reply_len = hostapd_ctrl_driver_flags(hapd->iface, reply, ++ reply_size); ++ } else if (os_strcmp(buf, "TERMINATE") == 0) { ++ eloop_terminate(); ++ } else if (os_strncmp(buf, "ACCEPT_ACL ", 11) == 0) { ++ if (os_strncmp(buf + 11, "ADD_MAC ", 8) == 0) { ++ if (!hostapd_ctrl_iface_acl_add_mac( ++ &hapd->conf->accept_mac, ++ &hapd->conf->num_accept_mac, buf + 19)) ++ hostapd_disassoc_accept_mac(hapd); ++ else ++ reply_len = -1; ++ } else if (os_strncmp((buf + 11), "DEL_MAC ", 8) == 0) { ++ hostapd_ctrl_iface_acl_del_mac( ++ &hapd->conf->accept_mac, ++ &hapd->conf->num_accept_mac, buf + 19); ++ } else if (os_strcmp(buf + 11, "SHOW") == 0) { ++ reply_len = hostapd_ctrl_iface_acl_show_mac( ++ hapd->conf->accept_mac, ++ hapd->conf->num_accept_mac, reply, reply_size); ++ } else if (os_strcmp(buf + 11, "CLEAR") == 0) { ++ hostapd_ctrl_iface_acl_clear_list( ++ &hapd->conf->accept_mac, ++ &hapd->conf->num_accept_mac); ++ } ++ } else if (os_strncmp(buf, "DENY_ACL ", 9) == 0) { ++ if (os_strncmp(buf + 9, "ADD_MAC ", 8) == 0) { ++ if (!hostapd_ctrl_iface_acl_add_mac( ++ &hapd->conf->deny_mac, ++ &hapd->conf->num_deny_mac, buf + 17)) ++ hostapd_disassoc_deny_mac(hapd); ++ } else if (os_strncmp(buf + 9, "DEL_MAC ", 8) == 0) { ++ hostapd_ctrl_iface_acl_del_mac( ++ &hapd->conf->deny_mac, ++ &hapd->conf->num_deny_mac, buf + 17); ++ } else if (os_strcmp(buf + 9, "SHOW") == 0) { ++ reply_len = hostapd_ctrl_iface_acl_show_mac( ++ hapd->conf->deny_mac, ++ hapd->conf->num_deny_mac, reply, reply_size); ++ } else if (os_strcmp(buf + 9, "CLEAR") == 0) { ++ hostapd_ctrl_iface_acl_clear_list( ++ &hapd->conf->deny_mac, ++ &hapd->conf->num_deny_mac); ++ } ++#ifdef CONFIG_DPP ++ } else if (os_strncmp(buf, "DPP_QR_CODE ", 12) == 0) { ++ res = hostapd_dpp_qr_code(hapd, buf + 12); ++ if (res < 0) { ++ reply_len = -1; ++ } else { ++ reply_len = os_snprintf(reply, reply_size, "%d", res); ++ if (os_snprintf_error(reply_size, reply_len)) ++ reply_len = -1; ++ } ++ } else if (os_strncmp(buf, "DPP_BOOTSTRAP_GEN ", 18) == 0) { ++ res = dpp_bootstrap_gen(hapd->iface->interfaces->dpp, buf + 18); ++ if (res < 0) { ++ reply_len = -1; ++ } else { ++ reply_len = os_snprintf(reply, reply_size, "%d", res); ++ if (os_snprintf_error(reply_size, reply_len)) ++ reply_len = -1; ++ } ++ } else if (os_strncmp(buf, "DPP_BOOTSTRAP_REMOVE ", 21) == 0) { ++ if (dpp_bootstrap_remove(hapd->iface->interfaces->dpp, ++ buf + 21) < 0) ++ reply_len = -1; ++ } else if (os_strncmp(buf, "DPP_BOOTSTRAP_GET_URI ", 22) == 0) { ++ const char *uri; ++ ++ uri = dpp_bootstrap_get_uri(hapd->iface->interfaces->dpp, ++ atoi(buf + 22)); ++ if (!uri) { ++ reply_len = -1; ++ } else { ++ reply_len = os_snprintf(reply, reply_size, "%s", uri); ++ if (os_snprintf_error(reply_size, reply_len)) ++ reply_len = -1; ++ } ++ } else if (os_strncmp(buf, "DPP_BOOTSTRAP_INFO ", 19) == 0) { ++ reply_len = dpp_bootstrap_info(hapd->iface->interfaces->dpp, ++ atoi(buf + 19), ++ reply, reply_size); ++ } else if (os_strncmp(buf, "DPP_AUTH_INIT ", 14) == 0) { ++ if (hostapd_dpp_auth_init(hapd, buf + 13) < 0) ++ reply_len = -1; ++ } else if (os_strncmp(buf, "DPP_LISTEN ", 11) == 0) { ++ if (hostapd_dpp_listen(hapd, buf + 11) < 0) ++ reply_len = -1; ++ } else if (os_strcmp(buf, "DPP_STOP_LISTEN") == 0) { ++ hostapd_dpp_stop(hapd); ++ hostapd_dpp_listen_stop(hapd); ++ } else if (os_strncmp(buf, "DPP_CONFIGURATOR_ADD", 20) == 0) { ++ res = dpp_configurator_add(hapd->iface->interfaces->dpp, ++ buf + 20); ++ if (res < 0) { ++ reply_len = -1; ++ } else { ++ reply_len = os_snprintf(reply, reply_size, "%d", res); ++ if (os_snprintf_error(reply_size, reply_len)) ++ reply_len = -1; ++ } ++ } else if (os_strncmp(buf, "DPP_CONFIGURATOR_REMOVE ", 24) == 0) { ++ if (dpp_configurator_remove(hapd->iface->interfaces->dpp, ++ buf + 24) < 0) ++ reply_len = -1; ++ } else if (os_strncmp(buf, "DPP_CONFIGURATOR_SIGN ", 22) == 0) { ++ if (hostapd_dpp_configurator_sign(hapd, buf + 21) < 0) ++ reply_len = -1; ++ } else if (os_strncmp(buf, "DPP_CONFIGURATOR_GET_KEY ", 25) == 0) { ++ reply_len = dpp_configurator_get_key_id( ++ hapd->iface->interfaces->dpp, ++ atoi(buf + 25), ++ reply, reply_size); ++ } else if (os_strncmp(buf, "DPP_PKEX_ADD ", 13) == 0) { ++ res = hostapd_dpp_pkex_add(hapd, buf + 12); ++ if (res < 0) { ++ reply_len = -1; ++ } else { ++ reply_len = os_snprintf(reply, reply_size, "%d", res); ++ if (os_snprintf_error(reply_size, reply_len)) ++ reply_len = -1; ++ } ++ } else if (os_strncmp(buf, "DPP_PKEX_REMOVE ", 16) == 0) { ++ if (hostapd_dpp_pkex_remove(hapd, buf + 16) < 0) ++ reply_len = -1; ++#endif /* CONFIG_DPP */ ++#ifdef RADIUS_SERVER ++ } else if (os_strncmp(buf, "DAC_REQUEST ", 12) == 0) { ++ if (radius_server_dac_request(hapd->radius_srv, buf + 12) < 0) ++ reply_len = -1; ++#endif /* RADIUS_SERVER */ ++ } else if (os_strncmp(buf, "GET_CAPABILITY ", 15) == 0) { ++ reply_len = hostapd_ctrl_iface_get_capability( ++ hapd, buf + 15, reply, reply_size); + } else { + os_memcpy(reply, "UNKNOWN COMMAND\n", 16); + reply_len = 16; +@@ -2296,12 +3370,15 @@ + struct hostapd_data *hapd = eloop_ctx; + char buf[4096]; + int res; +- struct sockaddr_un from; ++ struct sockaddr_storage from; + socklen_t fromlen = sizeof(from); +- char *reply; ++ char *reply, *pos = buf; + const int reply_size = 4096; + int reply_len; + int level = MSG_DEBUG; ++#ifdef CONFIG_CTRL_IFACE_UDP ++ unsigned char lcookie[COOKIE_LEN]; ++#endif /* CONFIG_CTRL_IFACE_UDP */ + + res = recvfrom(sock, buf, sizeof(buf) - 1, 0, + (struct sockaddr *) &from, &fromlen); +@@ -2311,9 +3388,6 @@ + return; + } + buf[res] = '\0'; +- if (os_strcmp(buf, "PING") == 0) +- level = MSG_EXCESSIVE; +- wpa_hexdump_ascii(level, "RX ctrl_iface", (u8 *) buf, res); + + reply = os_malloc(reply_size); + if (reply == NULL) { +@@ -2325,10 +3399,46 @@ + return; + } + +- reply_len = hostapd_ctrl_iface_receive_process(hapd, buf, ++#ifdef CONFIG_CTRL_IFACE_UDP ++ if (os_strcmp(buf, "GET_COOKIE") == 0) { ++ os_memcpy(reply, "COOKIE=", 7); ++ wpa_snprintf_hex(reply + 7, 2 * COOKIE_LEN + 1, ++ cookie, COOKIE_LEN); ++ reply_len = 7 + 2 * COOKIE_LEN; ++ goto done; ++ } ++ ++ if (os_strncmp(buf, "COOKIE=", 7) != 0 || ++ hexstr2bin(buf + 7, lcookie, COOKIE_LEN) < 0) { ++ wpa_printf(MSG_DEBUG, ++ "CTRL: No cookie in the request - drop request"); ++ os_free(reply); ++ return; ++ } ++ ++ if (os_memcmp(cookie, lcookie, COOKIE_LEN) != 0) { ++ wpa_printf(MSG_DEBUG, ++ "CTRL: Invalid cookie in the request - drop request"); ++ os_free(reply); ++ return; ++ } ++ ++ pos = buf + 7 + 2 * COOKIE_LEN; ++ while (*pos == ' ') ++ pos++; ++#endif /* CONFIG_CTRL_IFACE_UDP */ ++ ++ if (os_strcmp(pos, "PING") == 0) ++ level = MSG_EXCESSIVE; ++ wpa_hexdump_ascii(level, "RX ctrl_iface", pos, res); ++ ++ reply_len = hostapd_ctrl_iface_receive_process(hapd, pos, + reply, reply_size, + &from, fromlen); + ++#ifdef CONFIG_CTRL_IFACE_UDP ++done: ++#endif /* CONFIG_CTRL_IFACE_UDP */ + if (sendto(sock, reply, reply_len, 0, (struct sockaddr *) &from, + fromlen) < 0) { + wpa_printf(MSG_DEBUG, "CTRL: sendto failed: %s", +@@ -2338,6 +3448,7 @@ + } + + ++#ifndef CONFIG_CTRL_IFACE_UDP + static char * hostapd_ctrl_iface_path(struct hostapd_data *hapd) + { + char *buf; +@@ -2357,6 +3468,7 @@ + buf[len - 1] = '\0'; + return buf; + } ++#endif /* CONFIG_CTRL_IFACE_UDP */ + + + static void hostapd_ctrl_iface_msg_cb(void *ctx, int level, +@@ -2372,6 +3484,99 @@ + + int hostapd_ctrl_iface_init(struct hostapd_data *hapd) + { ++#ifdef CONFIG_CTRL_IFACE_UDP ++ int port = HOSTAPD_CTRL_IFACE_PORT; ++ char p[32] = { 0 }; ++ char port_str[40], *tmp; ++ char *pos; ++ struct addrinfo hints = { 0 }, *res, *saveres; ++ int n; ++ ++ if (hapd->ctrl_sock > -1) { ++ wpa_printf(MSG_DEBUG, "ctrl_iface already exists!"); ++ return 0; ++ } ++ ++ if (hapd->conf->ctrl_interface == NULL) ++ return 0; ++ ++ pos = os_strstr(hapd->conf->ctrl_interface, "udp:"); ++ if (pos) { ++ pos += 4; ++ port = atoi(pos); ++ if (port <= 0) { ++ wpa_printf(MSG_ERROR, "Invalid ctrl_iface UDP port"); ++ goto fail; ++ } ++ } ++ ++ dl_list_init(&hapd->ctrl_dst); ++ hapd->ctrl_sock = -1; ++ os_get_random(cookie, COOKIE_LEN); ++ ++#ifdef CONFIG_CTRL_IFACE_UDP_REMOTE ++ hints.ai_flags = AI_PASSIVE; ++#endif /* CONFIG_CTRL_IFACE_UDP_REMOTE */ ++ ++#ifdef CONFIG_CTRL_IFACE_UDP_IPV6 ++ hints.ai_family = AF_INET6; ++#else /* CONFIG_CTRL_IFACE_UDP_IPV6 */ ++ hints.ai_family = AF_INET; ++#endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */ ++ hints.ai_socktype = SOCK_DGRAM; ++ ++try_again: ++ os_snprintf(p, sizeof(p), "%d", port); ++ n = getaddrinfo(NULL, p, &hints, &res); ++ if (n) { ++ wpa_printf(MSG_ERROR, "getaddrinfo(): %s", gai_strerror(n)); ++ goto fail; ++ } ++ ++ saveres = res; ++ hapd->ctrl_sock = socket(res->ai_family, res->ai_socktype, ++ res->ai_protocol); ++ if (hapd->ctrl_sock < 0) { ++ wpa_printf(MSG_ERROR, "socket(PF_INET): %s", strerror(errno)); ++ goto fail; ++ } ++ ++ if (bind(hapd->ctrl_sock, res->ai_addr, res->ai_addrlen) < 0) { ++ port--; ++ if ((HOSTAPD_CTRL_IFACE_PORT - port) < ++ HOSTAPD_CTRL_IFACE_PORT_LIMIT && !pos) ++ goto try_again; ++ wpa_printf(MSG_ERROR, "bind(AF_INET): %s", strerror(errno)); ++ goto fail; ++ } ++ ++ freeaddrinfo(saveres); ++ ++ os_snprintf(port_str, sizeof(port_str), "udp:%d", port); ++ tmp = os_strdup(port_str); ++ if (tmp) { ++ os_free(hapd->conf->ctrl_interface); ++ hapd->conf->ctrl_interface = tmp; ++ } ++ wpa_printf(MSG_DEBUG, "ctrl_iface_init UDP port: %d", port); ++ ++ if (eloop_register_read_sock(hapd->ctrl_sock, ++ hostapd_ctrl_iface_receive, hapd, NULL) < ++ 0) { ++ hostapd_ctrl_iface_deinit(hapd); ++ return -1; ++ } ++ ++ hapd->msg_ctx = hapd; ++ wpa_msg_register_cb(hostapd_ctrl_iface_msg_cb); ++ ++ return 0; ++ ++fail: ++ if (hapd->ctrl_sock >= 0) ++ close(hapd->ctrl_sock); ++ return -1; ++#else /* CONFIG_CTRL_IFACE_UDP */ + struct sockaddr_un addr; + int s = -1; + char *fname = NULL; +@@ -2381,6 +3586,8 @@ + return 0; + } + ++ dl_list_init(&hapd->ctrl_dst); ++ + if (hapd->conf->ctrl_interface == NULL) + return 0; + +@@ -2396,9 +3603,9 @@ + } + + if (hapd->conf->ctrl_interface_gid_set && +- chown(hapd->conf->ctrl_interface, -1, +- hapd->conf->ctrl_interface_gid) < 0) { +- wpa_printf(MSG_ERROR, "chown[ctrl_interface]: %s", ++ lchown(hapd->conf->ctrl_interface, -1, ++ hapd->conf->ctrl_interface_gid) < 0) { ++ wpa_printf(MSG_ERROR, "lchown[ctrl_interface]: %s", + strerror(errno)); + return -1; + } +@@ -2405,9 +3612,9 @@ + + if (!hapd->conf->ctrl_interface_gid_set && + hapd->iface->interfaces->ctrl_iface_group && +- chown(hapd->conf->ctrl_interface, -1, +- hapd->iface->interfaces->ctrl_iface_group) < 0) { +- wpa_printf(MSG_ERROR, "chown[ctrl_interface]: %s", ++ lchown(hapd->conf->ctrl_interface, -1, ++ hapd->iface->interfaces->ctrl_iface_group) < 0) { ++ wpa_printf(MSG_ERROR, "lchown[ctrl_interface]: %s", + strerror(errno)); + return -1; + } +@@ -2480,8 +3687,8 @@ + } + + if (hapd->conf->ctrl_interface_gid_set && +- chown(fname, -1, hapd->conf->ctrl_interface_gid) < 0) { +- wpa_printf(MSG_ERROR, "chown[ctrl_interface/ifname]: %s", ++ lchown(fname, -1, hapd->conf->ctrl_interface_gid) < 0) { ++ wpa_printf(MSG_ERROR, "lchown[ctrl_interface/ifname]: %s", + strerror(errno)); + goto fail; + } +@@ -2488,8 +3695,8 @@ + + if (!hapd->conf->ctrl_interface_gid_set && + hapd->iface->interfaces->ctrl_iface_group && +- chown(fname, -1, hapd->iface->interfaces->ctrl_iface_group) < 0) { +- wpa_printf(MSG_ERROR, "chown[ctrl_interface/ifname]: %s", ++ lchown(fname, -1, hapd->iface->interfaces->ctrl_iface_group) < 0) { ++ wpa_printf(MSG_ERROR, "lchown[ctrl_interface/ifname]: %s", + strerror(errno)); + goto fail; + } +@@ -2520,6 +3727,7 @@ + os_free(fname); + } + return -1; ++#endif /* CONFIG_CTRL_IFACE_UDP */ + } + + +@@ -2528,10 +3736,14 @@ + struct wpa_ctrl_dst *dst, *prev; + + if (hapd->ctrl_sock > -1) { ++#ifndef CONFIG_CTRL_IFACE_UDP + char *fname; ++#endif /* !CONFIG_CTRL_IFACE_UDP */ ++ + eloop_unregister_read_sock(hapd->ctrl_sock); + close(hapd->ctrl_sock); + hapd->ctrl_sock = -1; ++#ifndef CONFIG_CTRL_IFACE_UDP + fname = hostapd_ctrl_iface_path(hapd); + if (fname) + unlink(fname); +@@ -2550,15 +3762,12 @@ + strerror(errno)); + } + } ++#endif /* !CONFIG_CTRL_IFACE_UDP */ + } + +- dst = hapd->ctrl_dst; +- hapd->ctrl_dst = NULL; +- while (dst) { +- prev = dst; +- dst = dst->next; +- os_free(prev); +- } ++ dl_list_for_each_safe(dst, prev, &hapd->ctrl_dst, struct wpa_ctrl_dst, ++ list) ++ os_free(dst); + + #ifdef CONFIG_TESTING_OPTIONS + l2_packet_deinit(hapd->l2_test); +@@ -2590,54 +3799,19 @@ + + + static int hostapd_global_ctrl_iface_attach(struct hapd_interfaces *interfaces, +- struct sockaddr_un *from, +- socklen_t fromlen) ++ struct sockaddr_storage *from, ++ socklen_t fromlen, char *input) + { +- struct wpa_ctrl_dst *dst; +- +- dst = os_zalloc(sizeof(*dst)); +- if (dst == NULL) +- return -1; +- os_memcpy(&dst->addr, from, sizeof(struct sockaddr_un)); +- dst->addrlen = fromlen; +- dst->debug_level = MSG_INFO; +- dst->next = interfaces->global_ctrl_dst; +- interfaces->global_ctrl_dst = dst; +- wpa_hexdump(MSG_DEBUG, "CTRL_IFACE monitor attached (global)", +- from->sun_path, +- fromlen - offsetof(struct sockaddr_un, sun_path)); +- return 0; ++ return ctrl_iface_attach(&interfaces->global_ctrl_dst, from, fromlen, ++ input); + } + + + static int hostapd_global_ctrl_iface_detach(struct hapd_interfaces *interfaces, +- struct sockaddr_un *from, ++ struct sockaddr_storage *from, + socklen_t fromlen) + { +- struct wpa_ctrl_dst *dst, *prev = NULL; +- +- dst = interfaces->global_ctrl_dst; +- while (dst) { +- if (fromlen == dst->addrlen && +- os_memcmp(from->sun_path, dst->addr.sun_path, +- fromlen - offsetof(struct sockaddr_un, sun_path)) +- == 0) { +- wpa_hexdump(MSG_DEBUG, +- "CTRL_IFACE monitor detached (global)", +- from->sun_path, +- fromlen - +- offsetof(struct sockaddr_un, sun_path)); +- if (prev == NULL) +- interfaces->global_ctrl_dst = dst->next; +- else +- prev->next = dst->next; +- os_free(dst); +- return 0; +- } +- prev = dst; +- dst = dst->next; +- } +- return -1; ++ return ctrl_iface_detach(&interfaces->global_ctrl_dst, from, fromlen); + } + + +@@ -2648,6 +3822,16 @@ + wps_testing_dummy_cred = 0; + wps_corrupt_pkhash = 0; + #endif /* CONFIG_WPS_TESTING */ ++ ++#ifdef CONFIG_TESTING_OPTIONS ++#ifdef CONFIG_DPP ++ dpp_test = DPP_TEST_DISABLED; ++#endif /* CONFIG_DPP */ ++#endif /* CONFIG_TESTING_OPTIONS */ ++ ++#ifdef CONFIG_DPP ++ dpp_global_clear(interfaces->dpp); ++#endif /* CONFIG_DPP */ + } + + +@@ -2791,6 +3975,51 @@ + + + static int ++hostapd_global_ctrl_iface_interfaces(struct hapd_interfaces *interfaces, ++ const char *input, ++ char *reply, int reply_size) ++{ ++ size_t i, j; ++ int res; ++ char *pos, *end; ++ struct hostapd_iface *iface; ++ int show_ctrl = 0; ++ ++ if (input) ++ show_ctrl = !!os_strstr(input, "ctrl"); ++ ++ pos = reply; ++ end = reply + reply_size; ++ ++ for (i = 0; i < interfaces->count; i++) { ++ iface = interfaces->iface[i]; ++ ++ for (j = 0; j < iface->num_bss; j++) { ++ struct hostapd_bss_config *conf; ++ ++ conf = iface->conf->bss[j]; ++ if (show_ctrl) ++ res = os_snprintf(pos, end - pos, ++ "%s ctrl_iface=%s\n", ++ conf->iface, ++ conf->ctrl_interface ? ++ conf->ctrl_interface : "N/A"); ++ else ++ res = os_snprintf(pos, end - pos, "%s\n", ++ conf->iface); ++ if (os_snprintf_error(end - pos, res)) { ++ *pos = '\0'; ++ return pos - reply; ++ } ++ pos += res; ++ } ++ } ++ ++ return pos - reply; ++} ++ ++ ++static int + hostapd_global_ctrl_iface_dup_network(struct hapd_interfaces *interfaces, + char *cmd) + { +@@ -2839,7 +4068,7 @@ + const char *ifname, + char *buf, char *reply, + int reply_size, +- struct sockaddr_un *from, ++ struct sockaddr_storage *from, + socklen_t fromlen) + { + struct hostapd_data *hapd; +@@ -2863,15 +4092,18 @@ + void *sock_ctx) + { + void *interfaces = eloop_ctx; +- char buf[256]; ++ char buffer[256], *buf = buffer; + int res; +- struct sockaddr_un from; ++ struct sockaddr_storage from; + socklen_t fromlen = sizeof(from); + char *reply; + int reply_len; + const int reply_size = 4096; ++#ifdef CONFIG_CTRL_IFACE_UDP ++ unsigned char lcookie[COOKIE_LEN]; ++#endif /* CONFIG_CTRL_IFACE_UDP */ + +- res = recvfrom(sock, buf, sizeof(buf) - 1, 0, ++ res = recvfrom(sock, buffer, sizeof(buffer) - 1, 0, + (struct sockaddr *) &from, &fromlen); + if (res < 0) { + wpa_printf(MSG_ERROR, "recvfrom(ctrl_iface): %s", +@@ -2894,6 +4126,35 @@ + os_memcpy(reply, "OK\n", 3); + reply_len = 3; + ++#ifdef CONFIG_CTRL_IFACE_UDP ++ if (os_strcmp(buf, "GET_COOKIE") == 0) { ++ os_memcpy(reply, "COOKIE=", 7); ++ wpa_snprintf_hex(reply + 7, 2 * COOKIE_LEN + 1, ++ gcookie, COOKIE_LEN); ++ reply_len = 7 + 2 * COOKIE_LEN; ++ goto send_reply; ++ } ++ ++ if (os_strncmp(buf, "COOKIE=", 7) != 0 || ++ hexstr2bin(buf + 7, lcookie, COOKIE_LEN) < 0) { ++ wpa_printf(MSG_DEBUG, ++ "CTRL: No cookie in the request - drop request"); ++ os_free(reply); ++ return; ++ } ++ ++ if (os_memcmp(gcookie, lcookie, COOKIE_LEN) != 0) { ++ wpa_printf(MSG_DEBUG, ++ "CTRL: Invalid cookie in the request - drop request"); ++ os_free(reply); ++ return; ++ } ++ ++ buf += 7 + 2 * COOKIE_LEN; ++ while (*buf == ' ') ++ buf++; ++#endif /* CONFIG_CTRL_IFACE_UDP */ ++ + if (os_strncmp(buf, "IFNAME=", 7) == 0) { + char *pos = os_strchr(buf + 7, ' '); + +@@ -2922,8 +4183,12 @@ + reply_len = -1; + } else if (os_strcmp(buf, "ATTACH") == 0) { + if (hostapd_global_ctrl_iface_attach(interfaces, &from, +- fromlen)) ++ fromlen, NULL)) + reply_len = -1; ++ } else if (os_strncmp(buf, "ATTACH ", 7) == 0) { ++ if (hostapd_global_ctrl_iface_attach(interfaces, &from, ++ fromlen, buf + 7)) ++ reply_len = -1; + } else if (os_strcmp(buf, "DETACH") == 0) { + if (hostapd_global_ctrl_iface_detach(interfaces, &from, + fromlen)) +@@ -2930,7 +4195,6 @@ + reply_len = -1; + #ifdef CONFIG_MODULE_TESTS + } else if (os_strcmp(buf, "MODULE_TESTS") == 0) { +- int hapd_module_tests(void); + if (hapd_module_tests() < 0) + reply_len = -1; + #endif /* CONFIG_MODULE_TESTS */ +@@ -2954,6 +4218,11 @@ + reply_len = os_snprintf(reply, reply_size, "OK\n"); + else + reply_len = -1; ++ } else if (os_strncmp(buf, "INTERFACES", 10) == 0) { ++ reply_len = hostapd_global_ctrl_iface_interfaces( ++ interfaces, buf + 10, reply, sizeof(buffer)); ++ } else if (os_strcmp(buf, "TERMINATE") == 0) { ++ eloop_terminate(); + } else { + wpa_printf(MSG_DEBUG, "Unrecognized global ctrl_iface command " + "ignored"); +@@ -2975,6 +4244,7 @@ + } + + ++#ifndef CONFIG_CTRL_IFACE_UDP + static char * hostapd_global_ctrl_iface_path(struct hapd_interfaces *interface) + { + char *buf; +@@ -2994,10 +4264,93 @@ + buf[len - 1] = '\0'; + return buf; + } ++#endif /* CONFIG_CTRL_IFACE_UDP */ + + + int hostapd_global_ctrl_iface_init(struct hapd_interfaces *interface) + { ++#ifdef CONFIG_CTRL_IFACE_UDP ++ int port = HOSTAPD_GLOBAL_CTRL_IFACE_PORT; ++ char p[32] = { 0 }; ++ char *pos; ++ struct addrinfo hints = { 0 }, *res, *saveres; ++ int n; ++ ++ if (interface->global_ctrl_sock > -1) { ++ wpa_printf(MSG_DEBUG, "ctrl_iface already exists!"); ++ return 0; ++ } ++ ++ if (interface->global_iface_path == NULL) ++ return 0; ++ ++ pos = os_strstr(interface->global_iface_path, "udp:"); ++ if (pos) { ++ pos += 4; ++ port = atoi(pos); ++ if (port <= 0) { ++ wpa_printf(MSG_ERROR, "Invalid global ctrl UDP port"); ++ goto fail; ++ } ++ } ++ ++ os_get_random(gcookie, COOKIE_LEN); ++ ++#ifdef CONFIG_CTRL_IFACE_UDP_REMOTE ++ hints.ai_flags = AI_PASSIVE; ++#endif /* CONFIG_CTRL_IFACE_UDP_REMOTE */ ++ ++#ifdef CONFIG_CTRL_IFACE_UDP_IPV6 ++ hints.ai_family = AF_INET6; ++#else /* CONFIG_CTRL_IFACE_UDP_IPV6 */ ++ hints.ai_family = AF_INET; ++#endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */ ++ hints.ai_socktype = SOCK_DGRAM; ++ ++try_again: ++ os_snprintf(p, sizeof(p), "%d", port); ++ n = getaddrinfo(NULL, p, &hints, &res); ++ if (n) { ++ wpa_printf(MSG_ERROR, "getaddrinfo(): %s", gai_strerror(n)); ++ goto fail; ++ } ++ ++ saveres = res; ++ interface->global_ctrl_sock = socket(res->ai_family, res->ai_socktype, ++ res->ai_protocol); ++ if (interface->global_ctrl_sock < 0) { ++ wpa_printf(MSG_ERROR, "socket(PF_INET): %s", strerror(errno)); ++ goto fail; ++ } ++ ++ if (bind(interface->global_ctrl_sock, res->ai_addr, res->ai_addrlen) < ++ 0) { ++ port++; ++ if ((port - HOSTAPD_GLOBAL_CTRL_IFACE_PORT) < ++ HOSTAPD_GLOBAL_CTRL_IFACE_PORT_LIMIT && !pos) ++ goto try_again; ++ wpa_printf(MSG_ERROR, "bind(AF_INET): %s", strerror(errno)); ++ goto fail; ++ } ++ ++ freeaddrinfo(saveres); ++ ++ wpa_printf(MSG_DEBUG, "global ctrl_iface_init UDP port: %d", port); ++ ++ if (eloop_register_read_sock(interface->global_ctrl_sock, ++ hostapd_global_ctrl_iface_receive, ++ interface, NULL) < 0) { ++ hostapd_global_ctrl_iface_deinit(interface); ++ return -1; ++ } ++ ++ return 0; ++ ++fail: ++ if (interface->global_ctrl_sock >= 0) ++ close(interface->global_ctrl_sock); ++ return -1; ++#else /* CONFIG_CTRL_IFACE_UDP */ + struct sockaddr_un addr; + int s = -1; + char *fname = NULL; +@@ -3017,9 +4370,9 @@ + goto fail; + } + } else if (interface->ctrl_iface_group && +- chown(interface->global_iface_path, -1, +- interface->ctrl_iface_group) < 0) { +- wpa_printf(MSG_ERROR, "chown[ctrl_interface]: %s", ++ lchown(interface->global_iface_path, -1, ++ interface->ctrl_iface_group) < 0) { ++ wpa_printf(MSG_ERROR, "lchown[ctrl_interface]: %s", + strerror(errno)); + goto fail; + } +@@ -3076,8 +4429,8 @@ + } + + if (interface->ctrl_iface_group && +- chown(fname, -1, interface->ctrl_iface_group) < 0) { +- wpa_printf(MSG_ERROR, "chown[ctrl_interface]: %s", ++ lchown(fname, -1, interface->ctrl_iface_group) < 0) { ++ wpa_printf(MSG_ERROR, "lchown[ctrl_interface]: %s", + strerror(errno)); + goto fail; + } +@@ -3103,12 +4456,15 @@ + os_free(fname); + } + return -1; ++#endif /* CONFIG_CTRL_IFACE_UDP */ + } + + + void hostapd_global_ctrl_iface_deinit(struct hapd_interfaces *interfaces) + { ++#ifndef CONFIG_CTRL_IFACE_UDP + char *fname = NULL; ++#endif /* CONFIG_CTRL_IFACE_UDP */ + struct wpa_ctrl_dst *dst, *prev; + + if (interfaces->global_ctrl_sock > -1) { +@@ -3115,6 +4471,7 @@ + eloop_unregister_read_sock(interfaces->global_ctrl_sock); + close(interfaces->global_ctrl_sock); + interfaces->global_ctrl_sock = -1; ++#ifndef CONFIG_CTRL_IFACE_UDP + fname = hostapd_global_ctrl_iface_path(interfaces); + if (fname) { + unlink(fname); +@@ -3134,26 +4491,36 @@ + strerror(errno)); + } + } ++#endif /* CONFIG_CTRL_IFACE_UDP */ + } + + os_free(interfaces->global_iface_path); + interfaces->global_iface_path = NULL; + +- dst = interfaces->global_ctrl_dst; +- interfaces->global_ctrl_dst = NULL; +- while (dst) { +- prev = dst; +- dst = dst->next; +- os_free(prev); +- } ++ dl_list_for_each_safe(dst, prev, &interfaces->global_ctrl_dst, ++ struct wpa_ctrl_dst, list) ++ os_free(dst); + } + + ++static int hostapd_ctrl_check_event_enabled(struct wpa_ctrl_dst *dst, ++ const char *buf) ++{ ++ /* Enable Probe Request events based on explicit request. ++ * Other events are enabled by default. ++ */ ++ if (str_starts(buf, RX_PROBE_REQUEST)) ++ return !!(dst->events & WPA_EVENT_RX_PROBE_REQUEST); ++ return 1; ++} ++ ++ + static void hostapd_ctrl_iface_send(struct hostapd_data *hapd, int level, + enum wpa_msg_type type, + const char *buf, size_t len) + { + struct wpa_ctrl_dst *dst, *next; ++ struct dl_list *ctrl_dst; + struct msghdr msg; + int idx; + struct iovec io[2]; +@@ -3162,13 +4529,13 @@ + + if (type != WPA_MSG_ONLY_GLOBAL) { + s = hapd->ctrl_sock; +- dst = hapd->ctrl_dst; ++ ctrl_dst = &hapd->ctrl_dst; + } else { + s = hapd->iface->interfaces->global_ctrl_sock; +- dst = hapd->iface->interfaces->global_ctrl_dst; ++ ctrl_dst = &hapd->iface->interfaces->global_ctrl_dst; + } + +- if (s < 0 || dst == NULL) ++ if (s < 0 || dl_list_empty(ctrl_dst)) + return; + + os_snprintf(levelstr, sizeof(levelstr), "<%d>", level); +@@ -3181,12 +4548,11 @@ + msg.msg_iovlen = 2; + + idx = 0; +- while (dst) { +- next = dst->next; +- if (level >= dst->debug_level) { +- wpa_hexdump(MSG_DEBUG, "CTRL_IFACE monitor send", +- (u8 *) dst->addr.sun_path, dst->addrlen - +- offsetof(struct sockaddr_un, sun_path)); ++ dl_list_for_each_safe(dst, next, ctrl_dst, struct wpa_ctrl_dst, list) { ++ if ((level >= dst->debug_level) && ++ hostapd_ctrl_check_event_enabled(dst, buf)) { ++ sockaddr_print(MSG_DEBUG, "CTRL_IFACE monitor send", ++ &dst->addr, dst->addrlen); + msg.msg_name = &dst->addr; + msg.msg_namelen = dst->addrlen; + if (sendmsg(s, &msg, 0) < 0) { +@@ -3210,7 +4576,6 @@ + dst->errors = 0; + } + idx++; +- dst = next; + } + } + +--- contrib/wpa/hostapd/defconfig.orig ++++ contrib/wpa/hostapd/defconfig +@@ -18,6 +18,9 @@ + # Driver interface for drivers using the nl80211 kernel interface + CONFIG_DRIVER_NL80211=y + ++# QCA vendor extensions to nl80211 ++#CONFIG_DRIVER_NL80211_QCA=y ++ + # driver_nl80211.c requires libnl. If you are compiling it yourself + # you may need to point hostapd to your version of libnl. + # +@@ -28,7 +31,7 @@ + #CONFIG_LIBNL20=y + + # Use libnl 3.2 libraries (if this is selected, CONFIG_LIBNL20 is ignored) +-#CONFIG_LIBNL32=y ++CONFIG_LIBNL32=y + + + # Driver interface for FreeBSD net80211 layer (e.g., Atheros driver) +@@ -47,12 +50,12 @@ + # WPA2/IEEE 802.11i RSN pre-authentication + CONFIG_RSN_PREAUTH=y + +-# PeerKey handshake for Station to Station Link (IEEE 802.11e DLS) +-CONFIG_PEERKEY=y +- + # IEEE 802.11w (management frame protection) + CONFIG_IEEE80211W=y + ++# Support Operating Channel Validation ++#CONFIG_OCV=y ++ + # Integrated EAP server + CONFIG_EAP=y + +@@ -154,6 +157,12 @@ + # IEEE 802.11ac (Very High Throughput) support + #CONFIG_IEEE80211AC=y + ++# IEEE 802.11ax HE support ++# Note: This is experimental and work in progress. The definitions are still ++# subject to change and this should not be expected to interoperate with the ++# final IEEE 802.11ax version. ++#CONFIG_IEEE80211AX=y ++ + # Remove debugging code that is printing out debug messages to stdout. + # This can be used to reduce the size of the hostapd considerably if debugging + # code is not needed. +@@ -163,6 +172,9 @@ + # Disabled by default. + #CONFIG_DEBUG_FILE=y + ++# Send debug messages to syslog instead of stdout ++#CONFIG_DEBUG_SYSLOG=y ++ + # Add support for sending all debug messages (regardless of debug verbosity) + # to the Linux kernel tracing facility. This helps debug the entire stack by + # making it easy to record everything happening from the driver up into the +@@ -240,6 +252,11 @@ + # requirements described above. + #CONFIG_NO_RANDOM_POOL=y + ++# Should we attempt to use the getrandom(2) call that provides more reliable ++# yet secure randomness source than /dev/random on Linux 3.17 and newer. ++# Requires glibc 2.25 to build, falls back to /dev/random if unavailable. ++#CONFIG_GETRANDOM=y ++ + # Should we use poll instead of select? Select is used by default. + #CONFIG_ELOOP_POLL=y + +@@ -246,10 +263,14 @@ + # Should we use epoll instead of select? Select is used by default. + #CONFIG_ELOOP_EPOLL=y + ++# Should we use kqueue instead of select? Select is used by default. ++#CONFIG_ELOOP_KQUEUE=y ++ + # Select TLS implementation + # openssl = OpenSSL (default) + # gnutls = GnuTLS + # internal = Internal TLSv1 implementation (experimental) ++# linux = Linux kernel AF_ALG and internal TLSv1 implementation (experimental) + # none = Empty template + #CONFIG_TLS=openssl + +@@ -262,6 +283,10 @@ + # can be enabled to enable use of stronger crypto algorithms. + #CONFIG_TLSV12=y + ++# Select which ciphers to use by default with OpenSSL if the user does not ++# specify them. ++#CONFIG_TLS_DEFAULT_CIPHERS="DEFAULT:!EXP:!LOW" ++ + # If CONFIG_TLS=internal is used, additional library and include paths are + # needed for LibTomMath. Alternatively, an integrated, minimal version of + # LibTomMath can be used. See beginning of libtommath.c for details on benefits +@@ -326,3 +351,31 @@ + # http://wireless.kernel.org/en/users/Documentation/acs + # + #CONFIG_ACS=y ++ ++# Multiband Operation support ++# These extentions facilitate efficient use of multiple frequency bands ++# available to the AP and the devices that may associate with it. ++#CONFIG_MBO=y ++ ++# Client Taxonomy ++# Has the AP retain the Probe Request and (Re)Association Request frames from ++# a client, from which a signature can be produced which can identify the model ++# of client device like "Nexus 6P" or "iPhone 5s". ++#CONFIG_TAXONOMY=y ++ ++# Fast Initial Link Setup (FILS) (IEEE 802.11ai) ++#CONFIG_FILS=y ++# FILS shared key authentication with PFS ++#CONFIG_FILS_SK_PFS=y ++ ++# Include internal line edit mode in hostapd_cli. This can be used to provide ++# limited command line editing and history support. ++#CONFIG_WPA_CLI_EDIT=y ++ ++# Opportunistic Wireless Encryption (OWE) ++# Experimental implementation of draft-harkins-owe-07.txt ++#CONFIG_OWE=y ++ ++# Override default value for the wpa_disable_eapol_key_retries configuration ++# parameter. See that parameter in hostapd.conf for more details. ++#CFLAGS += -DDEFAULT_WPA_DISABLE_EAPOL_KEY_RETRIES=1 +--- contrib/wpa/hostapd/hapd_module_tests.c.orig ++++ contrib/wpa/hostapd/hapd_module_tests.c +@@ -9,6 +9,7 @@ + #include "utils/includes.h" + + #include "utils/common.h" ++#include "utils/module_tests.h" + + int hapd_module_tests(void) + { +--- contrib/wpa/hostapd/hlr_auc_gw.c.orig ++++ contrib/wpa/hostapd/hlr_auc_gw.c +@@ -1,6 +1,6 @@ + /* + * HLR/AuC testing gateway for hostapd EAP-SIM/AKA database/authenticator +- * Copyright (c) 2005-2007, 2012-2013, Jouni Malinen ++ * Copyright (c) 2005-2007, 2012-2017, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. +@@ -284,7 +284,7 @@ + + f = fopen(fname, "r"); + if (f == NULL) { +- printf("Could not open GSM tripler data file '%s'\n", fname); ++ printf("Could not open GSM triplet data file '%s'\n", fname); + return -1; + } + +@@ -312,66 +312,40 @@ + } + + /* IMSI */ +- pos2 = strchr(pos, ':'); +- if (pos2 == NULL) { +- printf("%s:%d - Invalid IMSI (%s)\n", +- fname, line, pos); ++ pos2 = NULL; ++ pos = str_token(buf, ":", &pos2); ++ if (!pos || os_strlen(pos) >= sizeof(g->imsi)) { ++ printf("%s:%d - Invalid IMSI\n", fname, line); + ret = -1; + break; + } +- *pos2 = '\0'; +- if (strlen(pos) >= sizeof(g->imsi)) { +- printf("%s:%d - Too long IMSI (%s)\n", +- fname, line, pos); +- ret = -1; +- break; +- } + os_strlcpy(g->imsi, pos, sizeof(g->imsi)); +- pos = pos2 + 1; + + /* Kc */ +- pos2 = strchr(pos, ':'); +- if (pos2 == NULL) { +- printf("%s:%d - Invalid Kc (%s)\n", fname, line, pos); ++ pos = str_token(buf, ":", &pos2); ++ if (!pos || os_strlen(pos) != 16 || hexstr2bin(pos, g->kc, 8)) { ++ printf("%s:%d - Invalid Kc\n", fname, line); + ret = -1; + break; + } +- *pos2 = '\0'; +- if (strlen(pos) != 16 || hexstr2bin(pos, g->kc, 8)) { +- printf("%s:%d - Invalid Kc (%s)\n", fname, line, pos); +- ret = -1; +- break; +- } +- pos = pos2 + 1; + + /* SRES */ +- pos2 = strchr(pos, ':'); +- if (pos2 == NULL) { +- printf("%s:%d - Invalid SRES (%s)\n", fname, line, +- pos); ++ pos = str_token(buf, ":", &pos2); ++ if (!pos || os_strlen(pos) != 8 || ++ hexstr2bin(pos, g->sres, 4)) { ++ printf("%s:%d - Invalid SRES\n", fname, line); + ret = -1; + break; + } +- *pos2 = '\0'; +- if (strlen(pos) != 8 || hexstr2bin(pos, g->sres, 4)) { +- printf("%s:%d - Invalid SRES (%s)\n", fname, line, +- pos); +- ret = -1; +- break; +- } +- pos = pos2 + 1; + + /* RAND */ +- pos2 = strchr(pos, ':'); +- if (pos2) +- *pos2 = '\0'; +- if (strlen(pos) != 32 || hexstr2bin(pos, g->_rand, 16)) { +- printf("%s:%d - Invalid RAND (%s)\n", fname, line, +- pos); ++ pos = str_token(buf, ":", &pos2); ++ if (!pos || os_strlen(pos) != 32 || ++ hexstr2bin(pos, g->_rand, 16)) { ++ printf("%s:%d - Invalid RAND\n", fname, line); + ret = -1; + break; + } +- pos = pos2 + 1; + + g->next = gsm_db; + gsm_db = g; +@@ -450,86 +424,58 @@ + } + + /* IMSI */ +- pos2 = strchr(pos, ' '); +- if (pos2 == NULL) { +- printf("%s:%d - Invalid IMSI (%s)\n", +- fname, line, pos); ++ pos2 = NULL; ++ pos = str_token(buf, " ", &pos2); ++ if (!pos || os_strlen(pos) >= sizeof(m->imsi)) { ++ printf("%s:%d - Invalid IMSI\n", fname, line); + ret = -1; + break; + } +- *pos2 = '\0'; +- if (strlen(pos) >= sizeof(m->imsi)) { +- printf("%s:%d - Too long IMSI (%s)\n", +- fname, line, pos); +- ret = -1; +- break; +- } + os_strlcpy(m->imsi, pos, sizeof(m->imsi)); +- pos = pos2 + 1; + + /* Ki */ +- pos2 = strchr(pos, ' '); +- if (pos2 == NULL) { +- printf("%s:%d - Invalid Ki (%s)\n", fname, line, pos); ++ pos = str_token(buf, " ", &pos2); ++ if (!pos || os_strlen(pos) != 32 || ++ hexstr2bin(pos, m->ki, 16)) { ++ printf("%s:%d - Invalid Ki\n", fname, line); + ret = -1; + break; + } +- *pos2 = '\0'; +- if (strlen(pos) != 32 || hexstr2bin(pos, m->ki, 16)) { +- printf("%s:%d - Invalid Ki (%s)\n", fname, line, pos); +- ret = -1; +- break; +- } +- pos = pos2 + 1; + + /* OPc */ +- pos2 = strchr(pos, ' '); +- if (pos2 == NULL) { +- printf("%s:%d - Invalid OPc (%s)\n", fname, line, pos); ++ pos = str_token(buf, " ", &pos2); ++ if (!pos || os_strlen(pos) != 32 || ++ hexstr2bin(pos, m->opc, 16)) { ++ printf("%s:%d - Invalid OPc\n", fname, line); + ret = -1; + break; + } +- *pos2 = '\0'; +- if (strlen(pos) != 32 || hexstr2bin(pos, m->opc, 16)) { +- printf("%s:%d - Invalid OPc (%s)\n", fname, line, pos); +- ret = -1; +- break; +- } +- pos = pos2 + 1; + + /* AMF */ +- pos2 = strchr(pos, ' '); +- if (pos2 == NULL) { +- printf("%s:%d - Invalid AMF (%s)\n", fname, line, pos); ++ pos = str_token(buf, " ", &pos2); ++ if (!pos || os_strlen(pos) != 4 || hexstr2bin(pos, m->amf, 2)) { ++ printf("%s:%d - Invalid AMF\n", fname, line); + ret = -1; + break; + } +- *pos2 = '\0'; +- if (strlen(pos) != 4 || hexstr2bin(pos, m->amf, 2)) { +- printf("%s:%d - Invalid AMF (%s)\n", fname, line, pos); +- ret = -1; +- break; +- } +- pos = pos2 + 1; + + /* SQN */ +- pos2 = strchr(pos, ' '); +- if (pos2) +- *pos2 = '\0'; +- if (strlen(pos) != 12 || hexstr2bin(pos, m->sqn, 6)) { +- printf("%s:%d - Invalid SEQ (%s)\n", fname, line, pos); ++ pos = str_token(buf, " ", &pos2); ++ if (!pos || os_strlen(pos) != 12 || ++ hexstr2bin(pos, m->sqn, 6)) { ++ printf("%s:%d - Invalid SEQ\n", fname, line); + ret = -1; + break; + } + +- if (pos2) { +- pos = pos2 + 1; ++ pos = str_token(buf, " ", &pos2); ++ if (pos) { + m->res_len = atoi(pos); + if (m->res_len && + (m->res_len < EAP_AKA_RES_MIN_LEN || + m->res_len > EAP_AKA_RES_MAX_LEN)) { +- printf("%s:%d - Invalid RES_len (%s)\n", +- fname, line, pos); ++ printf("%s:%d - Invalid RES_len\n", ++ fname, line); + ret = -1; + break; + } +@@ -1027,7 +973,7 @@ + { + printf("HLR/AuC testing gateway for hostapd EAP-SIM/AKA " + "database/authenticator\n" +- "Copyright (c) 2005-2007, 2012-2013, Jouni Malinen \n" ++ "Copyright (c) 2005-2017, Jouni Malinen \n" + "\n" + "usage:\n" + "hlr_auc_gw [-hu] [-s] [-g] " +--- contrib/wpa/hostapd/hostapd.conf.orig ++++ contrib/wpa/hostapd/hostapd.conf +@@ -3,6 +3,8 @@ + + # AP netdevice name (without 'ap' postfix, i.e., wlan0 uses wlan0ap for + # management frames with the Host AP driver); wlan0 with many nl80211 drivers ++# Note: This attribute can be overridden by the values supplied with the '-i' ++# command line parameter. + interface=wlan0 + + # In case of atheros and nl80211 driver interfaces, an additional +@@ -96,8 +98,25 @@ + # Country code (ISO/IEC 3166-1). Used to set regulatory domain. + # Set as needed to indicate country in which device is operating. + # This can limit available channels and transmit power. ++# These two octets are used as the first two octets of the Country String ++# (dot11CountryString) + #country_code=US + ++# The third octet of the Country String (dot11CountryString) ++# This parameter is used to set the third octet of the country string. ++# ++# All environments of the current frequency band and country (default) ++#country3=0x20 ++# Outdoor environment only ++#country3=0x4f ++# Indoor environment only ++#country3=0x49 ++# Noncountry entity (country_code=XX) ++#country3=0x58 ++# IEEE 802.11 standard Annex E table indication: 0x01 .. 0x1f ++# Annex E, Table E-4 (Global operating classes) ++#country3=0x04 ++ + # Enable IEEE 802.11d. This advertises the country_code and the set of allowed + # channels and transmit power levels based on the regulatory limits. The + # country_code setting must be configured with the correct country for +@@ -125,11 +144,13 @@ + # ieee80211d=1 and local_pwr_constraint configured. + #spectrum_mgmt_required=1 + +-# Operation mode (a = IEEE 802.11a, b = IEEE 802.11b, g = IEEE 802.11g, +-# ad = IEEE 802.11ad (60 GHz); a/g options are used with IEEE 802.11n, too, to +-# specify band). When using ACS (see channel parameter), a special value "any" +-# can be used to indicate that any support band can be used. This special case +-# is currently supported only with drivers with which offloaded ACS is used. ++# Operation mode (a = IEEE 802.11a (5 GHz), b = IEEE 802.11b (2.4 GHz), ++# g = IEEE 802.11g (2.4 GHz), ad = IEEE 802.11ad (60 GHz); a/g options are used ++# with IEEE 802.11n (HT), too, to specify band). For IEEE 802.11ac (VHT), this ++# needs to be set to hw_mode=a. When using ACS (see channel parameter), a ++# special value "any" can be used to indicate that any support band can be used. ++# This special case is currently supported only with drivers with which ++# offloaded ACS is used. + # Default: IEEE 802.11b + hw_mode=g + +@@ -173,11 +194,16 @@ + # Channel list restriction. This option allows hostapd to select one of the + # provided channels when a channel should be automatically selected. + # Channel list can be provided as range using hyphen ('-') or individual +-# channels can be specified by space (' ') seperated values ++# channels can be specified by space (' ') separated values + # Default: all channels allowed in selected hw_mode + #chanlist=100 104 108 112 116 + #chanlist=1 6 11-13 + ++# Exclude DFS channels from ACS ++# This option can be used to exclude all DFS channels from the ACS channel list ++# in cases where the driver supports DFS channels. ++#acs_exclude_dfs=1 ++ + # Beacon interval in kus (1.024 ms) (default: 100; range 15..65535) + beacon_int=100 + +@@ -192,16 +218,16 @@ + # (default: 2007) + max_num_sta=255 + +-# RTS/CTS threshold; 2347 = disabled (default); range 0..2347 ++# RTS/CTS threshold; -1 = disabled (default); range -1..65535 + # If this field is not included in hostapd.conf, hostapd will not control + # RTS threshold and 'iwconfig wlan# rts ' can be used to set it. +-rts_threshold=2347 ++rts_threshold=-1 + +-# Fragmentation threshold; 2346 = disabled (default); range 256..2346 ++# Fragmentation threshold; -1 = disabled (default); range -1, 256..2346 + # If this field is not included in hostapd.conf, hostapd will not control + # fragmentation threshold and 'iwconfig wlan# frag ' can be used to set + # it. +-fragm_threshold=2346 ++fragm_threshold=-1 + + # Rate configuration + # Default is to enable all rates supported by the hardware. This configuration +@@ -223,6 +249,19 @@ + #basic_rates=10 20 55 110 + #basic_rates=60 120 240 + ++# Beacon frame TX rate configuration ++# This sets the TX rate that is used to transmit Beacon frames. If this item is ++# not included, the driver default rate (likely lowest rate) is used. ++# Legacy (CCK/OFDM rates): ++# beacon_rate= ++# HT: ++# beacon_rate=ht: ++# VHT: ++# beacon_rate=vht: ++# ++# For example, beacon_rate=10 for 1 Mbps or beacon_rate=60 for 6 Mbps (OFDM). ++#beacon_rate=10 ++ + # Short Preamble + # This parameter can be used to enable optional use of short preamble for + # frames sent at 2 Mbps, 5.5 Mbps, and 11 Mbps to improve network performance. +@@ -267,7 +306,14 @@ + # requests for broadcast SSID + ignore_broadcast_ssid=0 + +-# Additional vendor specfic elements for Beacon and Probe Response frames ++# Do not reply to broadcast Probe Request frames from unassociated STA if there ++# is no room for additional stations (max_num_sta). This can be used to ++# discourage a STA from trying to associate with this AP if the association ++# would be rejected due to maximum STA limit. ++# Default: 0 (disabled) ++#no_probe_resp_if_max_sta=0 ++ ++# Additional vendor specific elements for Beacon and Probe Response frames + # This parameter can be used to add additional vendor specific element(s) into + # the end of the Beacon and Probe Response frames. The format for these + # element(s) is a hexdump of the raw information elements (id+len+payload for +@@ -274,9 +320,16 @@ + # one or more elements) + #vendor_elements=dd0411223301 + ++# Additional vendor specific elements for (Re)Association Response frames ++# This parameter can be used to add additional vendor specific element(s) into ++# the end of the (Re)Association Response frames. The format for these ++# element(s) is a hexdump of the raw information elements (id+len+payload for ++# one or more elements) ++#assocresp_elements=dd0411223301 ++ + # TX queue parameters (EDCF / bursting) + # tx_queue__ +-# queues: data0, data1, data2, data3, after_beacon, beacon ++# queues: data0, data1, data2, data3 + # (data0 is the highest priority queue) + # parameters: + # aifs: AIFS (default 2) +@@ -385,6 +438,13 @@ + wmm_ac_vo_acm=0 + # Note: for IEEE 802.11b mode: cWmin=3 cWmax=4 burst=102 + ++# Enable Multi-AP functionality ++# 0 = disabled (default) ++# 1 = AP support backhaul BSS ++# 2 = AP support fronthaul BSS ++# 3 = AP supports both backhaul BSS and fronthaul BSS ++#multi_ap=0 ++ + # Static WEP key configuration + # + # The key number to use when transmitting. +@@ -458,6 +518,12 @@ + # Beacon and Probe Response frames. + #bss_load_update_period=50 + ++# Channel utilization averaging period (in BUs) ++# This field is used to enable and configure channel utilization average ++# calculation with bss_load_update_period. This should be in multiples of ++# bss_load_update_period for more accurate calculation. ++#chan_util_avg_period=600 ++ + # Fixed BSS Load value for testing purposes + # This field can be used to configure hostapd to add a fixed BSS Load element + # into Beacon and Probe Response frames for testing purposes. The format is +@@ -464,6 +530,26 @@ + # :: + #bss_load_test=12:80:20000 + ++# Multicast to unicast conversion ++# Request that the AP will do multicast-to-unicast conversion for ARP, IPv4, and ++# IPv6 frames (possibly within 802.1Q). If enabled, such frames are to be sent ++# to each station separately, with the DA replaced by their own MAC address ++# rather than the group address. ++# ++# Note that this may break certain expectations of the receiver, such as the ++# ability to drop unicast IP packets received within multicast L2 frames, or the ++# ability to not send ICMP destination unreachable messages for packets received ++# in L2 multicast (which is required, but the receiver can't tell the difference ++# if this new option is enabled). ++# ++# This also doesn't implement the 802.11 DMS (directed multicast service). ++# ++#multicast_to_unicast=0 ++ ++# Send broadcast Deauthentication frame on AP start/stop ++# Default: 1 (enabled) ++#broadcast_deauth=1 ++ + ##### IEEE 802.11n related configuration ###################################### + + # ieee80211n: Whether IEEE 802.11n (HT) is enabled +@@ -470,6 +556,7 @@ + # 0 = disabled (default) + # 1 = enabled + # Note: You will also need to enable WMM for full HT functionality. ++# Note: hw_mode=g (2.4 GHz) and hw_mode=a (5 GHz) is used to specify the band. + #ieee80211n=1 + + # ht_capab: HT capabilities (list of flags) +@@ -523,6 +610,7 @@ + # 0 = disabled (default) + # 1 = enabled + # Note: You will also need to enable WMM for full VHT functionality. ++# Note: hw_mode=a is used to specify that 5 GHz band is used with VHT. + #ieee80211ac=1 + + # vht_capab: VHT capabilities (list of flags) +@@ -605,9 +693,9 @@ + # VHT TXOP PS: [VHT-TXOP-PS] + # Indicates whether or not the AP supports VHT TXOP Power Save Mode + # or whether or not the STA is in VHT TXOP Power Save mode +-# 0 = VHT AP doesnt support VHT TXOP PS mode (OR) VHT Sta not in VHT TXOP PS ++# 0 = VHT AP doesn't support VHT TXOP PS mode (OR) VHT STA not in VHT TXOP PS + # mode +-# 1 = VHT AP supports VHT TXOP PS mode (OR) VHT Sta is in VHT TXOP power save ++# 1 = VHT AP supports VHT TXOP PS mode (OR) VHT STA is in VHT TXOP power save + # mode + # + # +HTC-VHT Capable: [HTC-VHT] +@@ -665,6 +753,78 @@ + # + #vht_oper_centr_freq_seg1_idx=159 + ++# Workaround to use station's nsts capability in (Re)Association Response frame ++# This may be needed with some deployed devices as an interoperability ++# workaround for beamforming if the AP's capability is greater than the ++# station's capability. This is disabled by default and can be enabled by ++# setting use_sta_nsts=1. ++#use_sta_nsts=0 ++ ++##### IEEE 802.11ax related configuration ##################################### ++ ++#ieee80211ax: Whether IEEE 802.11ax (HE) is enabled ++# 0 = disabled (default) ++# 1 = enabled ++#ieee80211ax=1 ++ ++#he_su_beamformer: HE single user beamformer support ++# 0 = not supported (default) ++# 1 = supported ++#he_su_beamformer=1 ++ ++#he_su_beamformee: HE single user beamformee support ++# 0 = not supported (default) ++# 1 = supported ++#he_su_beamformee=1 ++ ++#he_mu_beamformer: HE multiple user beamformer support ++# 0 = not supported (default) ++# 1 = supported ++#he_mu_beamformer=1 ++ ++# he_bss_color: BSS color ++# 0 = no BSS color (default) ++# unsigned integer = BSS color ++#he_bss_color=0 ++ ++#he_default_pe_duration: The duration of PE field in an HE PPDU in us ++# Possible values are 0 us (default), 4 us, 8 us, 12 us, and 16 us ++#he_default_pe_duration=0 ++ ++#he_twt_required: Whether TWT is required ++# 0 = not required (default) ++# 1 = required ++#he_twt_required=0 ++ ++#he_rts_threshold: Duration of STA transmission ++# 0 = not set (default) ++# unsigned integer = duration in units of 16 us ++#he_rts_threshold=0 ++ ++#he_mu_edca_qos_info_param_count ++#he_mu_edca_qos_info_q_ack ++#he_mu_edca_qos_info_queue_request=1 ++#he_mu_edca_qos_info_txop_request ++#he_mu_edca_ac_be_aifsn=0 ++#he_mu_edca_ac_be_ecwmin=15 ++#he_mu_edca_ac_be_ecwmax=15 ++#he_mu_edca_ac_be_timer=255 ++#he_mu_edca_ac_bk_aifsn=0 ++#he_mu_edca_ac_bk_aci=1 ++#he_mu_edca_ac_bk_ecwmin=15 ++#he_mu_edca_ac_bk_ecwmax=15 ++#he_mu_edca_ac_bk_timer=255 ++#he_mu_edca_ac_vi_ecwmin=15 ++#he_mu_edca_ac_vi_ecwmax=15 ++#he_mu_edca_ac_vi_aifsn=0 ++#he_mu_edca_ac_vi_aci=2 ++#he_mu_edca_ac_vi_timer=255 ++#he_mu_edca_ac_vo_aifsn=0 ++#he_mu_edca_ac_vo_aci=3 ++#he_mu_edca_ac_vo_ecwmin=15 ++#he_mu_edca_ac_vo_ecwmax=15 ++#he_mu_edca_ac_vo_timer=255 ++ + ##### IEEE 802.1X-2004 related configuration ################################## + + # Require IEEE 802.1X authorization +@@ -762,12 +922,56 @@ + # valid CRL signed by the CA is required to be included in the ca_cert file. + # This can be done by using PEM format for CA certificate and CRL and + # concatenating these into one file. Whenever CRL changes, hostapd needs to be +-# restarted to take the new CRL into use. ++# restarted to take the new CRL into use. Alternatively, crl_reload_interval can ++# be used to configure periodic updating of the loaded CRL information. + # 0 = do not verify CRLs (default) + # 1 = check the CRL of the user certificate + # 2 = check all CRLs in the certificate path + #check_crl=1 + ++# Specify whether to ignore certificate CRL validity time mismatches with ++# errors X509_V_ERR_CERT_HAS_EXPIRED and X509_V_ERR_CERT_NOT_YET_VALID. ++# ++# 0 = ignore errors ++# 1 = do not ignore errors (default) ++#check_crl_strict=1 ++ ++# CRL reload interval in seconds ++# This can be used to reload ca_cert file and the included CRL on every new TLS ++# session if difference between last reload and the current reload time in ++# seconds is greater than crl_reload_interval. ++# Note: If interval time is very short, CPU overhead may be negatively affected ++# and it is advised to not go below 300 seconds. ++# This is applicable only with check_crl values 1 and 2. ++# 0 = do not reload CRLs (default) ++# crl_reload_interval = 300 ++ ++# If check_cert_subject is set, the value of every field will be checked ++# against the DN of the subject in the client certificate. If the values do ++# not match, the certificate verification will fail, rejecting the user. ++# This option allows hostapd to match every individual field in the right order ++# against the DN of the subject in the client certificate. ++# ++# For example, check_cert_subject=C=US/O=XX/OU=ABC/OU=XYZ/CN=1234 will check ++# every individual DN field of the subject in the client certificate. If OU=XYZ ++# comes first in terms of the order in the client certificate (DN field of ++# client certificate C=US/O=XX/OU=XYZ/OU=ABC/CN=1234), hostapd will reject the ++# client because the order of 'OU' is not matching the specified string in ++# check_cert_subject. ++# ++# This option also allows '*' as a wildcard. This option has some limitation. ++# It can only be used as per the following example. ++# ++# For example, check_cert_subject=C=US/O=XX/OU=Production* and we have two ++# clients and DN of the subject in the first client certificate is ++# (C=US/O=XX/OU=Production Unit) and DN of the subject in the second client is ++# (C=US/O=XX/OU=Production Factory). In this case, hostapd will allow both ++# clients because the value of 'OU' field in both client certificates matches ++# 'OU' value in 'check_cert_subject' up to 'wildcard'. ++# ++# * (Allow all clients, e.g., check_cert_subject=*) ++#check_cert_subject=string ++ + # TLS Session Lifetime in seconds + # This can be used to allow TLS sessions to be cached and resumed with an + # abbreviated handshake when using EAP-TLS/TTLS/PEAP. +@@ -774,6 +978,27 @@ + # (default: 0 = session caching and resumption disabled) + #tls_session_lifetime=3600 + ++# TLS flags ++# [ALLOW-SIGN-RSA-MD5] = allow MD5-based certificate signatures (depending on ++# the TLS library, these may be disabled by default to enforce stronger ++# security) ++# [DISABLE-TIME-CHECKS] = ignore certificate validity time (this requests ++# the TLS library to accept certificates even if they are not currently ++# valid, i.e., have expired or have not yet become valid; this should be ++# used only for testing purposes) ++# [DISABLE-TLSv1.0] = disable use of TLSv1.0 ++# [ENABLE-TLSv1.0] = explicitly enable use of TLSv1.0 (this allows ++# systemwide TLS policies to be overridden) ++# [DISABLE-TLSv1.1] = disable use of TLSv1.1 ++# [ENABLE-TLSv1.1] = explicitly enable use of TLSv1.1 (this allows ++# systemwide TLS policies to be overridden) ++# [DISABLE-TLSv1.2] = disable use of TLSv1.2 ++# [ENABLE-TLSv1.2] = explicitly enable use of TLSv1.2 (this allows ++# systemwide TLS policies to be overridden) ++# [DISABLE-TLSv1.3] = disable use of TLSv1.3 ++# [ENABLE-TLSv1.3] = enable TLSv1.3 (experimental - disabled by default) ++#tls_flags=[flag1][flag2]... ++ + # Cached OCSP stapling response (DER encoded) + # If set, this file is sent as a certificate status response by the EAP server + # if the EAP peer requests certificate status in the ClientHello message. +@@ -788,6 +1013,11 @@ + # -respout /tmp/ocsp-cache.der + #ocsp_stapling_response=/tmp/ocsp-cache.der + ++# Cached OCSP stapling response list (DER encoded OCSPResponseList) ++# This is similar to ocsp_stapling_response, but the extended version defined in ++# RFC 6961 to allow multiple OCSP responses to be provided. ++#ocsp_stapling_response_multi=/tmp/ocsp-multi-cache.der ++ + # dh_file: File path to DH/DSA parameters file (in PEM format) + # This is an optional configuration file for setting parameters for an + # ephemeral DH key exchange. In most cases, the default RSA authentication does +@@ -803,12 +1033,26 @@ + # OpenSSL cipher string + # + # This is an OpenSSL specific configuration option for configuring the default +-# ciphers. If not set, "DEFAULT:!EXP:!LOW" is used as the default. ++# ciphers. If not set, the value configured at build time ("DEFAULT:!EXP:!LOW" ++# by default) is used. + # See https://www.openssl.org/docs/apps/ciphers.html for OpenSSL documentation + # on cipher suite configuration. This is applicable only if hostapd is built to + # use OpenSSL. + #openssl_ciphers=DEFAULT:!EXP:!LOW + ++# OpenSSL ECDH curves ++# ++# This is an OpenSSL specific configuration option for configuring the ECDH ++# curves for EAP-TLS/TTLS/PEAP/FAST server. If not set, automatic curve ++# selection is enabled. If set to an empty string, ECDH curve configuration is ++# not done (the exact library behavior depends on the library version). ++# Otherwise, this is a colon separated list of the supported curves (e.g., ++# P-521:P-384:P-256). This is applicable only if hostapd is built to use ++# OpenSSL. This must not be used for Suite B cases since the same OpenSSL ++# parameter is set differently in those cases and this might conflict with that ++# design. ++#openssl_ecdh_curves=P-521:P-384:P-256 ++ + # Fragment size for EAP methods + #fragment_size=1400 + +@@ -825,6 +1069,11 @@ + #eap_sim_db=unix:/tmp/hlr_auc_gw.sock + #eap_sim_db=unix:/tmp/hlr_auc_gw.sock db=/tmp/hostapd.db + ++# EAP-SIM DB request timeout ++# This parameter sets the maximum time to wait for a database request response. ++# The parameter value is in seconds. ++#eap_sim_db_timeout=1 ++ + # Encryption key for EAP-FAST PAC-Opaque values. This key must be a secret, + # random value. It is configured as a 16-octet value in hex format. It can be + # generated, e.g., with the following command: +@@ -888,11 +1137,23 @@ + # The own IP address of the access point (used as NAS-IP-Address) + own_ip_addr=127.0.0.1 + +-# Optional NAS-Identifier string for RADIUS messages. When used, this should be +-# a unique to the NAS within the scope of the RADIUS server. For example, a +-# fully qualified domain name can be used here. ++# NAS-Identifier string for RADIUS messages. When used, this should be unique ++# to the NAS within the scope of the RADIUS server. Please note that hostapd ++# uses a separate RADIUS client for each BSS and as such, a unique ++# nas_identifier value should be configured separately for each BSS. This is ++# particularly important for cases where RADIUS accounting is used ++# (Accounting-On/Off messages are interpreted as clearing all ongoing sessions ++# and that may get interpreted as applying to all BSSes if the same ++# NAS-Identifier value is used.) For example, a fully qualified domain name ++# prefixed with a unique identifier of the BSS (e.g., BSSID) can be used here. ++# + # When using IEEE 802.11r, nas_identifier must be set and must be between 1 and + # 48 octets long. ++# ++# It is mandatory to configure either own_ip_addr or nas_identifier to be ++# compliant with the RADIUS protocol. When using RADIUS accounting, it is ++# strongly recommended that nas_identifier is set to a unique value for each ++# BSS. + #nas_identifier=ap.example.com + + # RADIUS client forced local IP address for the access point +@@ -952,11 +1213,24 @@ + # Tunnel-Medium-Type (value 6 = IEEE 802), Tunnel-Private-Group-ID (value + # VLANID as a string). Optionally, the local MAC ACL list (accept_mac_file) can + # be used to set static client MAC address to VLAN ID mapping. +-# 0 = disabled (default) +-# 1 = option; use default interface if RADIUS server does not include VLAN ID ++# Dynamic VLAN mode is also used with VLAN ID assignment based on WPA/WPA2 ++# passphrase from wpa_psk_file or vlan_id parameter from sae_password. ++# 0 = disabled (default); only VLAN IDs from accept_mac_file will be used ++# 1 = optional; use default interface if RADIUS server does not include VLAN ID + # 2 = required; reject authentication if RADIUS server does not include VLAN ID + #dynamic_vlan=0 + ++# Per-Station AP_VLAN interface mode ++# If enabled, each station is assigned its own AP_VLAN interface. ++# This implies per-station group keying and ebtables filtering of inter-STA ++# traffic (when passed through the AP). ++# If the sta is not assigned to any VLAN, then its AP_VLAN interface will be ++# added to the bridge given by the "bridge" configuration option (see above). ++# Otherwise, it will be added to the per-VLAN bridge. ++# 0 = disabled (default) ++# 1 = enabled ++#per_sta_vif=0 ++ + # VLAN interface list for dynamic VLAN mode is read from a separate text file. + # This list is used to map VLAN ID from the RADIUS server to a network + # interface. Each station is bound to one interface in the same way as with +@@ -965,6 +1239,7 @@ + # white space (space or tab). + # If no entries are provided by this file, the station is statically mapped + # to . interfaces. ++# Each line can optionally also contain the name of a bridge to add the VLAN to + #vlan_file=/etc/hostapd.vlan + + # Interface where 802.1q tagged packets should appear when a RADIUS server is +@@ -1028,6 +1303,8 @@ + #radius_das_port=3799 + # + # DAS client (the host that can send Disconnect/CoA requests) and shared secret ++# Format: ++# IP address 0.0.0.0 can be used to allow requests from any address. + #radius_das_client=192.168.1.123 shared secret here + # + # DAS Event-Timestamp time window in seconds +@@ -1035,6 +1312,9 @@ + # + # DAS require Event-Timestamp + #radius_das_require_event_timestamp=1 ++# ++# DAS require Message-Authenticator ++#radius_das_require_message_authenticator=1 + + ##### RADIUS authentication server configuration ############################## + +@@ -1071,7 +1351,10 @@ + # and/or WPA2 (full IEEE 802.11i/RSN): + # bit0 = WPA + # bit1 = IEEE 802.11i/RSN (WPA2) (dot11RSNAEnabled) +-#wpa=1 ++# Note that WPA3 is also configured with bit1 since it uses RSN just like WPA2. ++# In other words, for WPA3, wpa=2 is used the configuration (and ++# wpa_key_mgmt=SAE for WPA3-Personal instead of wpa_key_mgmt=WPA-PSK). ++#wpa=2 + + # WPA pre-shared keys for WPA-PSK. This can be either entered as a 256-bit + # secret in hex format (64 hex digits), wpa_psk, or as an ASCII passphrase +@@ -1100,17 +1383,39 @@ + # Set of accepted key management algorithms (WPA-PSK, WPA-EAP, or both). The + # entries are separated with a space. WPA-PSK-SHA256 and WPA-EAP-SHA256 can be + # added to enable SHA256-based stronger algorithms. ++# WPA-PSK = WPA-Personal / WPA2-Personal ++# WPA-PSK-SHA256 = WPA2-Personal using SHA256 ++# WPA-EAP = WPA-Enterprise / WPA2-Enterprise ++# WPA-EAP-SHA256 = WPA2-Enterprise using SHA256 ++# SAE = SAE (WPA3-Personal) ++# WPA-EAP-SUITE-B-192 = WPA3-Enterprise with 192-bit security/CNSA suite ++# FT-PSK = FT with passphrase/PSK ++# FT-EAP = FT with EAP ++# FT-EAP-SHA384 = FT with EAP using SHA384 ++# FT-SAE = FT with SAE ++# FILS-SHA256 = Fast Initial Link Setup with SHA256 ++# FILS-SHA384 = Fast Initial Link Setup with SHA384 ++# FT-FILS-SHA256 = FT and Fast Initial Link Setup with SHA256 ++# FT-FILS-SHA384 = FT and Fast Initial Link Setup with SHA384 ++# OWE = Opportunistic Wireless Encryption (a.k.a. Enhanced Open) ++# DPP = Device Provisioning Protocol ++# OSEN = Hotspot 2.0 online signup with encryption + # (dot11RSNAConfigAuthenticationSuitesTable) + #wpa_key_mgmt=WPA-PSK WPA-EAP + + # Set of accepted cipher suites (encryption algorithms) for pairwise keys + # (unicast packets). This is a space separated list of algorithms: +-# CCMP = AES in Counter mode with CBC-MAC [RFC 3610, IEEE 802.11i/D7.0] +-# TKIP = Temporal Key Integrity Protocol [IEEE 802.11i/D7.0] ++# CCMP = AES in Counter mode with CBC-MAC (CCMP-128) ++# TKIP = Temporal Key Integrity Protocol ++# CCMP-256 = AES in Counter mode with CBC-MAC with 256-bit key ++# GCMP = Galois/counter mode protocol (GCMP-128) ++# GCMP-256 = Galois/counter mode protocol with 256-bit key + # Group cipher suite (encryption algorithm for broadcast and multicast frames) + # is automatically selected based on this configuration. If only CCMP is + # allowed as the pairwise cipher, group cipher will also be CCMP. Otherwise, +-# TKIP will be used as the group cipher. ++# TKIP will be used as the group cipher. The optional group_cipher parameter can ++# be used to override this automatic selection. ++# + # (dot11RSNAConfigPairwiseCiphersTable) + # Pairwise cipher for WPA (v1) (default: TKIP) + #wpa_pairwise=TKIP CCMP +@@ -1117,14 +1422,34 @@ + # Pairwise cipher for RSN/WPA2 (default: use wpa_pairwise value) + #rsn_pairwise=CCMP + ++# Optional override for automatic group cipher selection ++# This can be used to select a specific group cipher regardless of which ++# pairwise ciphers were enabled for WPA and RSN. It should be noted that ++# overriding the group cipher with an unexpected value can result in ++# interoperability issues and in general, this parameter is mainly used for ++# testing purposes. ++#group_cipher=CCMP ++ + # Time interval for rekeying GTK (broadcast/multicast encryption keys) in + # seconds. (dot11RSNAConfigGroupRekeyTime) +-#wpa_group_rekey=600 ++# This defaults to 86400 seconds (once per day) when using CCMP/GCMP as the ++# group cipher and 600 seconds (once per 10 minutes) when using TKIP as the ++# group cipher. ++#wpa_group_rekey=86400 + + # Rekey GTK when any STA that possesses the current GTK is leaving the BSS. + # (dot11RSNAConfigGroupRekeyStrict) + #wpa_strict_rekey=1 + ++# The number of times EAPOL-Key Message 1/2 in the RSN Group Key Handshake is ++#retried per GTK Handshake attempt. (dot11RSNAConfigGroupUpdateCount) ++# This value should only be increased when stations are constantly ++# deauthenticated during GTK rekeying with the log message ++# "group key handshake failed...". ++# You should consider to also increase wpa_pairwise_update_count then. ++# Range 1..4294967295; default: 4 ++#wpa_group_update_count=4 ++ + # Time interval for rekeying GMK (master key used internally to generate GTKs + # (in seconds). + #wpa_gmk_rekey=86400 +@@ -1133,6 +1458,36 @@ + # PTK to mitigate some attacks against TKIP deficiencies. + #wpa_ptk_rekey=600 + ++# The number of times EAPOL-Key Message 1/4 and Message 3/4 in the RSN 4-Way ++# Handshake are retried per 4-Way Handshake attempt. ++# (dot11RSNAConfigPairwiseUpdateCount) ++# Range 1..4294967295; default: 4 ++#wpa_pairwise_update_count=4 ++ ++# Workaround for key reinstallation attacks ++# ++# This parameter can be used to disable retransmission of EAPOL-Key frames that ++# are used to install keys (EAPOL-Key message 3/4 and group message 1/2). This ++# is similar to setting wpa_group_update_count=1 and ++# wpa_pairwise_update_count=1, but with no impact to message 1/4 and with ++# extended timeout on the response to avoid causing issues with stations that ++# may use aggressive power saving have very long time in replying to the ++# EAPOL-Key messages. ++# ++# This option can be used to work around key reinstallation attacks on the ++# station (supplicant) side in cases those station devices cannot be updated ++# for some reason. By removing the retransmissions the attacker cannot cause ++# key reinstallation with a delayed frame transmission. This is related to the ++# station side vulnerabilities CVE-2017-13077, CVE-2017-13078, CVE-2017-13079, ++# CVE-2017-13080, and CVE-2017-13081. ++# ++# This workaround might cause interoperability issues and reduced robustness of ++# key negotiation especially in environments with heavy traffic load due to the ++# number of attempts to perform the key exchange is reduced significantly. As ++# such, this workaround is disabled by default (unless overridden in build ++# configuration). To enable this, set the parameter to 1. ++#wpa_disable_eapol_key_retries=1 ++ + # Enable IEEE 802.11i/RSN/WPA2 pre-authentication. This is used to speed up + # roaming be pre-authenticating IEEE 802.1X/EAP part of the full RSN + # authentication and key handshake before actually associating with a new AP. +@@ -1148,12 +1503,6 @@ + # one. + #rsn_preauth_interfaces=eth0 + +-# peerkey: Whether PeerKey negotiation for direct links (IEEE 802.11e) is +-# allowed. This is only used with RSN/WPA2. +-# 0 = disabled (default) +-# 1 = enabled +-#peerkey=1 +- + # ieee80211w: Whether management frame protection (MFP) is enabled + # 0 = disabled (default) + # 1 = optional +@@ -1181,6 +1530,13 @@ + # dot11AssociationSAQueryRetryTimeout, 1...4294967295 + #assoc_sa_query_retry_timeout=201 + ++# ocv: Operating Channel Validation ++# This is a countermeasure against multi-channel man-in-the-middle attacks. ++# Enabling this automatically also enables ieee80211w, if not yet enabled. ++# 0 = disabled (default) ++# 1 = enabled ++#ocv=1 ++ + # disable_pmksa_caching: Disable PMKSA caching + # This parameter can be used to disable caching of PMKSA created through EAP + # authentication. RSN preauthentication may still end up using PMKSA caching if +@@ -1196,20 +1552,134 @@ + # 1 = enabled + #okc=1 + ++# SAE password ++# This parameter can be used to set passwords for SAE. By default, the ++# wpa_passphrase value is used if this separate parameter is not used, but ++# wpa_passphrase follows the WPA-PSK constraints (8..63 characters) even though ++# SAE passwords do not have such constraints. If the BSS enabled both SAE and ++# WPA-PSK and both values are set, SAE uses the sae_password values and WPA-PSK ++# uses the wpa_passphrase value. ++# ++# Each sae_password entry is added to a list of available passwords. This ++# corresponds to the dot11RSNAConfigPasswordValueEntry. sae_password value ++# starts with the password (dot11RSNAConfigPasswordCredential). That value can ++# be followed by optional peer MAC address (dot11RSNAConfigPasswordPeerMac) and ++# by optional password identifier (dot11RSNAConfigPasswordIdentifier). In ++# addition, an optional VLAN ID specification can be used to bind the station ++# to the specified VLAN whenver the specific SAE password entry is used. ++# ++# If the peer MAC address is not included or is set to the wildcard address ++# (ff:ff:ff:ff:ff:ff), the entry is available for any station to use. If a ++# specific peer MAC address is included, only a station with that MAC address ++# is allowed to use the entry. ++# ++# If the password identifier (with non-zero length) is included, the entry is ++# limited to be used only with that specified identifier. ++ ++# The last matching (based on peer MAC address and identifier) entry is used to ++# select which password to use. Setting sae_password to an empty string has a ++# special meaning of removing all previously added entries. ++# ++# sae_password uses the following encoding: ++#[|mac=][|vlanid=][|id=] ++# Examples: ++#sae_password=secret ++#sae_password=really secret|mac=ff:ff:ff:ff:ff:ff ++#sae_password=example secret|mac=02:03:04:05:06:07|id=pw identifier ++#sae_password=example secret|vlanid=3|id=pw identifier ++ + # SAE threshold for anti-clogging mechanism (dot11RSNASAEAntiCloggingThreshold) + # This parameter defines how many open SAE instances can be in progress at the + # same time before the anti-clogging mechanism is taken into use. + #sae_anti_clogging_threshold=5 + ++# Maximum number of SAE synchronization errors (dot11RSNASAESync) ++# The offending SAe peer will be disconnected if more than this many ++# synchronization errors happen. ++#sae_sync=5 ++ + # Enabled SAE finite cyclic groups + # SAE implementation are required to support group 19 (ECC group defined over a +-# 256-bit prime order field). All groups that are supported by the +-# implementation are enabled by default. This configuration parameter can be ++# 256-bit prime order field). This configuration parameter can be used to ++# specify a set of allowed groups. If not included, only the mandatory group 19 ++# is enabled. ++# The group values are listed in the IANA registry: ++# http://www.iana.org/assignments/ipsec-registry/ipsec-registry.xml#ipsec-registry-9 ++# Note that groups 1, 2, 5, 22, 23, and 24 should not be used in production ++# purposes due limited security (see RFC 8247). Groups that are not as strong as ++# group 19 (ECC, NIST P-256) are unlikely to be useful for production use cases ++# since all implementations are required to support group 19. ++#sae_groups=19 20 21 ++ ++# Require MFP for all associations using SAE ++# This parameter can be used to enforce negotiation of MFP for all associations ++# that negotiate use of SAE. This is used in cases where SAE-capable devices are ++# known to be MFP-capable and the BSS is configured with optional MFP ++# (ieee80211w=1) for legacy support. The non-SAE stations can connect without ++# MFP while SAE stations are required to negotiate MFP if sae_require_mfp=1. ++#sae_require_mfp=0 ++ ++# FILS Cache Identifier (16-bit value in hexdump format) ++#fils_cache_id=0011 ++ ++# FILS Realm Information ++# One or more FILS realms need to be configured when FILS is enabled. This list ++# of realms is used to define which realms (used in keyName-NAI by the client) ++# can be used with FILS shared key authentication for ERP. ++#fils_realm=example.com ++#fils_realm=example.org ++ ++# FILS DH Group for PFS ++# 0 = PFS disabled with FILS shared key authentication (default) ++# 1-65535 DH Group to use for FILS PFS ++#fils_dh_group=0 ++ ++# OWE DH groups ++# OWE implementations are required to support group 19 (NIST P-256). All groups ++# that are supported by the implementation (e.g., groups 19, 20, and 21 when ++# using OpenSSL) are enabled by default. This configuration parameter can be + # used to specify a limited set of allowed groups. The group values are listed + # in the IANA registry: +-# http://www.iana.org/assignments/ipsec-registry/ipsec-registry.xml#ipsec-registry-9 +-#sae_groups=19 20 21 25 26 ++# http://www.iana.org/assignments/ipsec-registry/ipsec-registry.xml#ipsec-registry-10 ++#owe_groups=19 20 21 + ++# OWE transition mode configuration ++# Pointer to the matching open/OWE BSS ++#owe_transition_bssid= ++# SSID in same format as ssid2 described above. ++#owe_transition_ssid= ++# Alternatively, OWE transition mode BSSID/SSID can be configured with a ++# reference to a BSS operated by this hostapd process. ++#owe_transition_ifname= ++ ++# DHCP server for FILS HLP ++# If configured, hostapd will act as a DHCP relay for all FILS HLP requests ++# that include a DHCPDISCOVER message and send them to the specific DHCP ++# server for processing. hostapd will then wait for a response from that server ++# before replying with (Re)Association Response frame that encapsulates this ++# DHCP response. own_ip_addr is used as the local address for the communication ++# with the DHCP server. ++#dhcp_server=127.0.0.1 ++ ++# DHCP server UDP port ++# Default: 67 ++#dhcp_server_port=67 ++ ++# DHCP relay UDP port on the local device ++# Default: 67; 0 means not to bind any specific port ++#dhcp_relay_port=67 ++ ++# DHCP rapid commit proxy ++# If set to 1, this enables hostapd to act as a DHCP rapid commit proxy to ++# allow the rapid commit options (two message DHCP exchange) to be used with a ++# server that supports only the four message DHCP exchange. This is disabled by ++# default (= 0) and can be enabled by setting this to 1. ++#dhcp_rapid_commit_proxy=0 ++ ++# Wait time for FILS HLP (dot11HLPWaitTime) in TUs ++# default: 30 TUs (= 30.72 milliseconds) ++#fils_hlp_wait_time=30 ++ + ##### IEEE 802.11r configuration ############################################## + + # Mobility Domain identifier (dot11FTMobilityDomainID, MDID) +@@ -1222,12 +1692,20 @@ + # 1 to 48 octet identifier. + # This is configured with nas_identifier (see RADIUS client section above). + +-# Default lifetime of the PMK-RO in minutes; range 1..65535 ++# Default lifetime of the PMK-R0 in seconds; range 60..4294967295 ++# (default: 14 days / 1209600 seconds; 0 = disable timeout) + # (dot11FTR0KeyLifetime) +-#r0_key_lifetime=10000 ++#ft_r0_key_lifetime=1209600 + ++# Maximum lifetime for PMK-R1; applied only if not zero ++# PMK-R1 is removed at latest after this limit. ++# Removing any PMK-R1 for expiry can be disabled by setting this to -1. ++# (default: 0) ++#r1_max_key_lifetime=0 ++ + # PMK-R1 Key Holder identifier (dot11FTR1KeyHolderID) + # 6-octet identifier as a hex string. ++# Defaults to BSSID. + #r1_key_holder=000102030405 + + # Reassociation deadline in time units (TUs / 1.024 ms; range 1000..65535) +@@ -1235,23 +1713,53 @@ + #reassociation_deadline=1000 + + # List of R0KHs in the same Mobility Domain +-# format: <128-bit key as hex string> ++# format: <256-bit key as hex string> + # This list is used to map R0KH-ID (NAS Identifier) to a destination MAC + # address when requesting PMK-R1 key from the R0KH that the STA used during the + # Initial Mobility Domain Association. +-#r0kh=02:01:02:03:04:05 r0kh-1.example.com 000102030405060708090a0b0c0d0e0f +-#r0kh=02:01:02:03:04:06 r0kh-2.example.com 00112233445566778899aabbccddeeff ++#r0kh=02:01:02:03:04:05 r0kh-1.example.com 000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f ++#r0kh=02:01:02:03:04:06 r0kh-2.example.com 00112233445566778899aabbccddeeff00112233445566778899aabbccddeeff + # And so on.. One line per R0KH. ++# Wildcard entry: ++# Upon receiving a response from R0KH, it will be added to this list, so ++# subsequent requests won't be broadcast. If R0KH does not reply, it will be ++# blacklisted. ++#r0kh=ff:ff:ff:ff:ff:ff * 00112233445566778899aabbccddeeff + + # List of R1KHs in the same Mobility Domain +-# format: <128-bit key as hex string> ++# format: <256-bit key as hex string> + # This list is used to map R1KH-ID to a destination MAC address when sending + # PMK-R1 key from the R0KH. This is also the list of authorized R1KHs in the MD + # that can request PMK-R1 keys. +-#r1kh=02:01:02:03:04:05 02:11:22:33:44:55 000102030405060708090a0b0c0d0e0f +-#r1kh=02:01:02:03:04:06 02:11:22:33:44:66 00112233445566778899aabbccddeeff ++#r1kh=02:01:02:03:04:05 02:11:22:33:44:55 000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f ++#r1kh=02:01:02:03:04:06 02:11:22:33:44:66 00112233445566778899aabbccddeeff00112233445566778899aabbccddeeff + # And so on.. One line per R1KH. ++# Wildcard entry: ++# Upon receiving a request from an R1KH not yet known, it will be added to this ++# list and thus will receive push notifications. ++#r1kh=00:00:00:00:00:00 00:00:00:00:00:00 00112233445566778899aabbccddeeff + ++# Timeout (seconds) for newly discovered R0KH/R1KH (see wildcard entries above) ++# Special values: 0 -> do not expire ++# Warning: do not cache implies no sequence number validation with wildcards ++#rkh_pos_timeout=86400 (default = 1 day) ++ ++# Timeout (milliseconds) for requesting PMK-R1 from R0KH using PULL request ++# and number of retries. ++#rkh_pull_timeout=1000 (default = 1 second) ++#rkh_pull_retries=4 (default) ++ ++# Timeout (seconds) for non replying R0KH (see wildcard entries above) ++# Special values: 0 -> do not cache ++# default: 60 seconds ++#rkh_neg_timeout=60 ++ ++# Note: The R0KH/R1KH keys used to be 128-bit in length before the message ++# format was changed. That shorter key length is still supported for backwards ++# compatibility of the configuration files. If such a shorter key is used, a ++# 256-bit key is derived from it. For new deployments, configuring the 256-bit ++# key is recommended. ++ + # Whether PMK-R1 push is enabled at R0KH + # 0 = do not push PMK-R1 to all configured R1KHs (default) + # 1 = push PMK-R1 to all configured R1KHs whenever a new PMK-R0 is derived +@@ -1262,6 +1770,14 @@ + # 1 = FT-over-DS enabled (default) + #ft_over_ds=1 + ++# Whether to generate FT response locally for PSK networks ++# This avoids use of PMK-R1 push/pull from other APs with FT-PSK networks as ++# the required information (PSK and other session data) is already locally ++# available. ++# 0 = disabled (default) ++# 1 = enabled ++#ft_psk_generate_local=0 ++ + ##### Neighbor table ########################################################## + # Maximum number of entries kept in AP table (either for neigbor table or for + # detecting Overlapping Legacy BSS Condition). The oldest entry will be +@@ -1452,6 +1968,14 @@ + # the configuration appropriately in this case. + #wps_cred_processing=0 + ++# Whether to enable SAE (WPA3-Personal transition mode) automatically for ++# WPA2-PSK credentials received using WPS. ++# 0 = only add the explicitly listed WPA2-PSK configuration (default) ++# 1 = add both the WPA2-PSK and SAE configuration and enable PMF so that the ++# AP gets configured in WPA3-Personal transition mode (supports both ++# WPA2-Personal (PSK) and WPA3-Personal (SAE) clients). ++#wps_cred_add_sae=0 ++ + # AP Settings Attributes for M7 + # By default, hostapd generates the AP Settings Attributes for M7 based on the + # current configuration. It is possible to override this by providing a file +@@ -1460,6 +1984,15 @@ + # attribute. + #ap_settings=hostapd.ap_settings + ++# Multi-AP backhaul BSS config ++# Used in WPS when multi_ap=2 or 3. Defines "backhaul BSS" credentials. ++# These are passed in WPS M8 instead of the normal (fronthaul) credentials ++# if the Enrollee has the Multi-AP subelement set. Backhaul SSID is formatted ++# like ssid2. The key is set like wpa_psk or wpa_passphrase. ++#multi_ap_backhaul_ssid="backhaul" ++#multi_ap_backhaul_wpa_psk=0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef ++#multi_ap_backhaul_wpa_passphrase=secret passphrase ++ + # WPS UPnP interface + # If set, support for external Registrars is enabled. + #upnp_iface=br0 +@@ -1532,6 +2065,18 @@ + # 1 = enabled (allow stations to use WNM-Sleep Mode) + #wnm_sleep_mode=1 + ++# WNM-Sleep Mode GTK/IGTK workaround ++# Normally, WNM-Sleep Mode exit with management frame protection negotiated ++# would result in the current GTK/IGTK getting added into the WNM-Sleep Mode ++# Response frame. Some station implementations may have a vulnerability that ++# results in GTK/IGTK reinstallation based on this frame being replayed. This ++# configuration parameter can be used to disable that behavior and use EAPOL-Key ++# frames for GTK/IGTK update instead. This would likely be only used with ++# wpa_disable_eapol_key_retries=1 that enables a workaround for similar issues ++# with EAPOL-Key. This is related to station side vulnerabilities CVE-2017-13087 ++# and CVE-2017-13088. To enable this AP-side workaround, set the parameter to 1. ++#wnm_sleep_mode_no_keys=0 ++ + # BSS Transition Management + # 0 = disabled (default) + # 1 = enabled +@@ -1619,6 +2164,15 @@ + # (double quoted string, printf-escaped string) + #venue_name=P"eng:Example\nvenue" + ++# Venue URL information ++# This parameter can be used to configure one or more Venue URL Duples to ++# provide additional information corresponding to Venue Name information. ++# Each entry has a Venue Number value separated by colon from the Venue URL ++# string. Venue Number indicates the corresponding venue_name entry (1 = 1st ++# venue_name, 2 = 2nd venue_name, and so on; 0 = no matching venue_name) ++#venue_url=1:http://www.example.com/info-eng ++#venue_url=2:http://www.example.com/info-fin ++ + # Network Authentication Type + # This parameter indicates what type of network authentication is used in the + # network. +@@ -1684,6 +2238,24 @@ + # username/password + #nai_realm=0,example.org,13[5:6],21[2:4][5:7] + ++# Arbitrary ANQP-element configuration ++# Additional ANQP-elements with arbitrary values can be defined by specifying ++# their contents in raw format as a hexdump of the payload. Note that these ++# values will override ANQP-element contents that may have been specified in the ++# more higher layer configuration parameters listed above. ++# format: anqp_elem=: ++# For example, AP Geospatial Location ANQP-element with unknown location: ++#anqp_elem=265:0000 ++# For example, AP Civic Location ANQP-element with unknown location: ++#anqp_elem=266:000000 ++ ++# GAS Address 3 behavior ++# 0 = P2P specification (Address3 = AP BSSID) workaround enabled by default ++# based on GAS request Address3 ++# 1 = IEEE 802.11 standard compliant regardless of GAS request Address3 ++# 2 = Force non-compliant behavior (Address3 = AP BSSID for all cases) ++#gas_address3=0 ++ + # QoS Map Set configuration + # + # Comma delimited QoS Map Set in decimal values +@@ -1771,7 +2343,27 @@ + # channels 36-48): + #hs20_operating_class=5173 + +-# OSU icons ++# Terms and Conditions information ++# ++# hs20_t_c_filename contains the Terms and Conditions filename that the AP ++# indicates in RADIUS Access-Request messages. ++#hs20_t_c_filename=terms-and-conditions ++# ++# hs20_t_c_timestamp contains the Terms and Conditions timestamp that the AP ++# indicates in RADIUS Access-Request messages. Usually, this contains the number ++# of seconds since January 1, 1970 00:00 UTC showing the time when the file was ++# last modified. ++#hs20_t_c_timestamp=1234567 ++# ++# hs20_t_c_server_url contains a template for the Terms and Conditions server ++# URL. This template is used to generate the URL for a STA that needs to ++# acknowledge Terms and Conditions. Unlike the other hs20_t_c_* parameters, this ++# parameter is used on the authentication server, not the AP. ++# Macros: ++# @1@ = MAC address of the STA (colon separated hex octets) ++#hs20_t_c_server_url=https://example.com/t_and_c?addr=@1@&ap=123 ++ ++# OSU and Operator icons + # ::::: + #hs20_icon=32:32:eng:image/png:icon32:/tmp/icon32.png + #hs20_icon=64:64:eng:image/png:icon64:/tmp/icon64.png +@@ -1783,12 +2375,15 @@ + # OSU Providers + # One or more sets of following parameter. Each OSU provider is started by the + # mandatory osu_server_uri item. The other parameters add information for the +-# last added OSU provider. ++# last added OSU provider. osu_nai specifies the OSU_NAI value for OSEN ++# authentication when using a standalone OSU BSS. osu_nai2 specifies the OSU_NAI ++# value for OSEN authentication when using a shared BSS (Single SSID) for OSU. + # + #osu_server_uri=https://example.com/osu/ + #osu_friendly_name=eng:Example operator + #osu_friendly_name=fin:Esimerkkipalveluntarjoaja + #osu_nai=anonymous@example.com ++#osu_nai2=anonymous@example.com + #osu_method_list=1 0 + #osu_icon=icon32 + #osu_icon=icon64 +@@ -1797,6 +2392,50 @@ + # + #osu_server_uri=... + ++# Operator Icons ++# Operator icons are specified using references to the hs20_icon entries ++# (Name subfield). This information, if present, is advertsised in the ++# Operator Icon Metadata ANQO-element. ++#operator_icon=icon32 ++#operator_icon=icon64 ++ ++##### Multiband Operation (MBO) ############################################### ++# ++# MBO enabled ++# 0 = disabled (default) ++# 1 = enabled ++#mbo=1 ++# ++# Cellular data connection preference ++# 0 = Excluded - AP does not want STA to use the cellular data connection ++# 1 = AP prefers the STA not to use cellular data connection ++# 255 = AP prefers the STA to use cellular data connection ++#mbo_cell_data_conn_pref=1 ++ ++##### Optimized Connectivity Experience (OCE) ################################# ++# ++# Enable OCE specific features (bitmap) ++# BIT(0) - Reserved ++# Set BIT(1) (= 2) to enable OCE in STA-CFON mode ++# Set BIT(2) (= 4) to enable OCE in AP mode ++# Default is 0 = OCE disabled ++#oce=0 ++ ++# RSSI-based assocition rejection ++# ++# Reject STA association if RSSI is below given threshold (in dBm) ++# Allowed range: -60 to -90 dBm; default = 0 (rejection disabled) ++# Note: This rejection happens based on a signal strength detected while ++# receiving a single frame and as such, there is significant risk of the value ++# not being accurate and this resulting in valid stations being rejected. As ++# such, this functionality is not recommended to be used for purposes other than ++# testing. ++#rssi_reject_assoc_rssi=-75 ++# ++# Association retry delay in seconds allowed by the STA if RSSI has not met the ++# threshold (range: 0..255, default=30). ++#rssi_reject_assoc_timeout=30 ++ + ##### Fast Session Transfer (FST) support ##################################### + # + # The options in this section are only available when the build configuration +@@ -1823,6 +2462,36 @@ + # Transitioning between states). + #fst_llt=100 + ++##### Radio measurements / location ########################################### ++ ++# The content of a LCI measurement subelement ++#lci= ++ ++# The content of a location civic measurement subelement ++#civic= ++ ++# Enable neighbor report via radio measurements ++#rrm_neighbor_report=1 ++ ++# Enable beacon report via radio measurements ++#rrm_beacon_report=1 ++ ++# Publish fine timing measurement (FTM) responder functionality ++# This parameter only controls publishing via Extended Capabilities element. ++# Actual functionality is managed outside hostapd. ++#ftm_responder=0 ++ ++# Publish fine timing measurement (FTM) initiator functionality ++# This parameter only controls publishing via Extended Capabilities element. ++# Actual functionality is managed outside hostapd. ++#ftm_initiator=0 ++# ++# Stationary AP config indicates that the AP doesn't move hence location data ++# can be considered as always up to date. If configured, LCI data will be sent ++# as a radio measurement even if the request doesn't contain a max age element ++# that allows sending of such data. Default: 0. ++#stationary_ap=0 ++ + ##### TESTING OPTIONS ######################################################### + # + # The options in this section are only available when the build configuration +@@ -1844,6 +2513,10 @@ + # + # Corrupt Key MIC in GTK rekey EAPOL-Key frames with the given probability + #corrupt_gtk_rekey_mic_probability=0.0 ++# ++# Include only ECSA IE without CSA IE where possible ++# (channel switch operating class is needed) ++#ecsa_ie_only=0 + + ##### Multiple BSSID support ################################################## + # +@@ -1866,6 +2539,10 @@ + # - is not the same as the MAC address of the radio + # - is not the same as any other explicitly specified BSSID + # ++# Alternatively, the 'use_driver_iface_addr' parameter can be used to request ++# hostapd to use the driver auto-generated interface address (e.g., to use the ++# exact MAC addresses allocated to the device). ++# + # Not all drivers support multiple BSSes. The exact mechanism for determining + # the driver capabilities is driver specific. With the current (i.e., a recent + # kernel) drivers using nl80211, this information can be checked with "iw list" +--- contrib/wpa/hostapd/hostapd.eap_user_sqlite.orig ++++ contrib/wpa/hostapd/hostapd.eap_user_sqlite +@@ -3,7 +3,8 @@ + methods TEXT, + password TEXT, + remediation TEXT, +- phase2 INTEGER ++ phase2 INTEGER, ++ t_c_timestamp INTEGER + ); + + CREATE TABLE wildcards( +@@ -24,3 +25,18 @@ + username TEXT, + note TEXT + ); ++ ++CREATE TABLE pending_tc( ++ mac_addr TEXT PRIMARY KEY, ++ identity TEXT ++); ++ ++CREATE TABLE current_sessions( ++ mac_addr TEXT PRIMARY KEY, ++ identity TEXT, ++ start_time TEXT, ++ nas TEXT, ++ hs20_t_c_filtering BOOLEAN, ++ waiting_coa_ack BOOLEAN, ++ coa_ack_received BOOLEAN ++); +--- contrib/wpa/hostapd/hostapd.wpa_psk.orig ++++ contrib/wpa/hostapd/hostapd.wpa_psk +@@ -3,7 +3,13 @@ + # Special MAC address 00:00:00:00:00:00 can be used to configure PSKs that + # anyone can use. PSK can be configured as an ASCII passphrase of 8..63 + # characters or as a 256-bit hex PSK (64 hex digits). ++# An optional key identifier can be added by prefixing the line with ++# keyid= ++# An optional VLAN ID can be specified by prefixing the line with ++# vlanid=. + 00:00:00:00:00:00 secret passphrase + 00:11:22:33:44:55 another passphrase + 00:22:33:44:55:66 0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef ++keyid=example_id 00:11:22:33:44:77 passphrase with keyid ++vlanid=3 00:00:00:00:00:00 passphrase with vlanid + 00:00:00:00:00:00 another passphrase for all STAs +--- contrib/wpa/hostapd/hostapd_cli.c.orig ++++ contrib/wpa/hostapd/hostapd_cli.c +@@ -1,6 +1,6 @@ + /* + * hostapd - command line interface for hostapd daemon +- * Copyright (c) 2004-2015, Jouni Malinen ++ * Copyright (c) 2004-2019, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. +@@ -15,80 +15,14 @@ + #include "utils/eloop.h" + #include "utils/edit.h" + #include "common/version.h" ++#include "common/cli.h" + ++#ifndef CONFIG_NO_CTRL_IFACE + + static const char *const hostapd_cli_version = + "hostapd_cli v" VERSION_STR "\n" +-"Copyright (c) 2004-2015, Jouni Malinen and contributors"; ++"Copyright (c) 2004-2019, Jouni Malinen and contributors"; + +- +-static const char *const hostapd_cli_license = +-"This software may be distributed under the terms of the BSD license.\n" +-"See README for more details.\n"; +- +-static const char *const hostapd_cli_full_license = +-"This software may be distributed under the terms of the BSD license.\n" +-"\n" +-"Redistribution and use in source and binary forms, with or without\n" +-"modification, are permitted provided that the following conditions are\n" +-"met:\n" +-"\n" +-"1. Redistributions of source code must retain the above copyright\n" +-" notice, this list of conditions and the following disclaimer.\n" +-"\n" +-"2. Redistributions in binary form must reproduce the above copyright\n" +-" notice, this list of conditions and the following disclaimer in the\n" +-" documentation and/or other materials provided with the distribution.\n" +-"\n" +-"3. Neither the name(s) of the above-listed copyright holder(s) nor the\n" +-" names of its contributors may be used to endorse or promote products\n" +-" derived from this software without specific prior written permission.\n" +-"\n" +-"THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n" +-"\"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n" +-"LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n" +-"A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n" +-"OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n" +-"SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\n" +-"LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n" +-"DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\n" +-"THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n" +-"(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n" +-"OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n" +-"\n"; +- +-static const char *const commands_help = +-"Commands:\n" +-" mib get MIB variables (dot1x, dot11, radius)\n" +-" sta get MIB variables for one station\n" +-" all_sta get MIB variables for all stations\n" +-" new_sta add a new station\n" +-" deauthenticate deauthenticate a station\n" +-" disassociate disassociate a station\n" +-#ifdef CONFIG_IEEE80211W +-" sa_query send SA Query to a station\n" +-#endif /* CONFIG_IEEE80211W */ +-#ifdef CONFIG_WPS +-" wps_pin [timeout] [addr] add WPS Enrollee PIN\n" +-" wps_check_pin verify PIN checksum\n" +-" wps_pbc indicate button pushed to initiate PBC\n" +-" wps_cancel cancel the pending WPS operation\n" +-#ifdef CONFIG_WPS_NFC +-" wps_nfc_tag_read report read NFC tag with WPS data\n" +-" wps_nfc_config_token build NFC configuration token\n" +-" wps_nfc_token manager NFC password token\n" +-#endif /* CONFIG_WPS_NFC */ +-" wps_ap_pin [params..] enable/disable AP PIN\n" +-" wps_config configure AP\n" +-" wps_get_status show current WPS status\n" +-#endif /* CONFIG_WPS */ +-" get_config show current configuration\n" +-" help show this usage help\n" +-" interface [ifname] show interfaces/select interface\n" +-" level change debug level\n" +-" license show full hostapd_cli license\n" +-" quit exit hostapd_cli\n"; +- + static struct wpa_ctrl *ctrl_conn; + static int hostapd_cli_quit = 0; + static int hostapd_cli_attached = 0; +@@ -104,8 +38,17 @@ + static const char *action_file = NULL; + static int ping_interval = 5; + static int interactive = 0; ++static int event_handler_registered = 0; + ++static DEFINE_DL_LIST(stations); /* struct cli_txt_entry */ + ++static void print_help(FILE *stream, const char *cmd); ++static char ** list_cmd_list(void); ++static void hostapd_cli_receive(int sock, void *eloop_ctx, void *sock_ctx); ++static void update_stations(struct wpa_ctrl *ctrl); ++static void cli_event(const char *str); ++ ++ + static void usage(void) + { + fprintf(stderr, "%s\n", hostapd_cli_version); +@@ -128,20 +71,49 @@ + " -B run a daemon in the background\n" + " -i Interface to listen on (default: first " + "interface found in the\n" +- " socket path)\n\n" +- "%s", +- commands_help); ++ " socket path)\n\n"); ++ print_help(stderr, NULL); + } + + ++static void register_event_handler(struct wpa_ctrl *ctrl) ++{ ++ if (!ctrl_conn) ++ return; ++ if (interactive) { ++ event_handler_registered = ++ !eloop_register_read_sock(wpa_ctrl_get_fd(ctrl), ++ hostapd_cli_receive, ++ NULL, NULL); ++ } ++} ++ ++ ++static void unregister_event_handler(struct wpa_ctrl *ctrl) ++{ ++ if (!ctrl_conn) ++ return; ++ if (interactive && event_handler_registered) { ++ eloop_unregister_read_sock(wpa_ctrl_get_fd(ctrl)); ++ event_handler_registered = 0; ++ } ++} ++ ++ + static struct wpa_ctrl * hostapd_cli_open_connection(const char *ifname) + { ++#ifndef CONFIG_CTRL_IFACE_UDP + char *cfile; + int flen; ++#endif /* !CONFIG_CTRL_IFACE_UDP */ + + if (ifname == NULL) + return NULL; + ++#ifdef CONFIG_CTRL_IFACE_UDP ++ ctrl_conn = wpa_ctrl_open(ifname); ++ return ctrl_conn; ++#else /* CONFIG_CTRL_IFACE_UDP */ + flen = strlen(ctrl_iface_dir) + strlen(ifname) + 2; + cfile = malloc(flen); + if (cfile == NULL) +@@ -158,6 +130,7 @@ + ctrl_conn = wpa_ctrl_open2(cfile, client_socket_dir); + free(cfile); + return ctrl_conn; ++#endif /* CONFIG_CTRL_IFACE_UDP */ + } + + +@@ -166,6 +139,7 @@ + if (ctrl_conn == NULL) + return; + ++ unregister_event_handler(ctrl_conn); + if (hostapd_cli_attached) { + wpa_ctrl_detach(ctrl_conn); + hostapd_cli_attached = 0; +@@ -175,13 +149,45 @@ + } + + ++static int hostapd_cli_reconnect(const char *ifname) ++{ ++ char *next_ctrl_ifname; ++ ++ hostapd_cli_close_connection(); ++ ++ if (!ifname) ++ return -1; ++ ++ next_ctrl_ifname = os_strdup(ifname); ++ os_free(ctrl_ifname); ++ ctrl_ifname = next_ctrl_ifname; ++ if (!ctrl_ifname) ++ return -1; ++ ++ ctrl_conn = hostapd_cli_open_connection(ctrl_ifname); ++ if (!ctrl_conn) ++ return -1; ++ if (!interactive && !action_file) ++ return 0; ++ if (wpa_ctrl_attach(ctrl_conn) == 0) { ++ hostapd_cli_attached = 1; ++ register_event_handler(ctrl_conn); ++ update_stations(ctrl_conn); ++ } else { ++ printf("Warning: Failed to attach to hostapd.\n"); ++ } ++ return 0; ++} ++ ++ + static void hostapd_cli_msg_cb(char *msg, size_t len) + { ++ cli_event(msg); + printf("%s\n", msg); + } + + +-static int _wpa_ctrl_command(struct wpa_ctrl *ctrl, char *cmd, int print) ++static int _wpa_ctrl_command(struct wpa_ctrl *ctrl, const char *cmd, int print) + { + char buf[4096]; + size_t len; +@@ -209,12 +215,28 @@ + } + + +-static inline int wpa_ctrl_command(struct wpa_ctrl *ctrl, char *cmd) ++static inline int wpa_ctrl_command(struct wpa_ctrl *ctrl, const char *cmd) + { + return _wpa_ctrl_command(ctrl, cmd, 1); + } + + ++static int hostapd_cli_cmd(struct wpa_ctrl *ctrl, const char *cmd, ++ int min_args, int argc, char *argv[]) ++{ ++ char buf[4096]; ++ ++ if (argc < min_args) { ++ printf("Invalid %s command - at least %d argument%s required.\n", ++ cmd, min_args, min_args > 1 ? "s are" : " is"); ++ return -1; ++ } ++ if (write_cmd(buf, sizeof(buf), cmd, argc, argv) < 0) ++ return -1; ++ return wpa_ctrl_command(ctrl, buf); ++} ++ ++ + static int hostapd_cli_cmd_ping(struct wpa_ctrl *ctrl, int argc, char *argv[]) + { + return wpa_ctrl_command(ctrl, "PING"); +@@ -298,6 +320,21 @@ + } + + ++static char ** hostapd_complete_stations(const char *str, int pos) ++{ ++ int arg = get_cmd_arg_num(str, pos); ++ char **res = NULL; ++ ++ switch (arg) { ++ case 1: ++ res = cli_txt_list_array(&stations); ++ break; ++ } ++ ++ return res; ++} ++ ++ + static int hostapd_cli_cmd_new_sta(struct wpa_ctrl *ctrl, int argc, + char *argv[]) + { +@@ -348,6 +385,22 @@ + } + + ++#ifdef CONFIG_TAXONOMY ++static int hostapd_cli_cmd_signature(struct wpa_ctrl *ctrl, int argc, ++ char *argv[]) ++{ ++ char buf[64]; ++ ++ if (argc != 1) { ++ printf("Invalid 'signature' command - exactly one argument, STA address, is required.\n"); ++ return -1; ++ } ++ os_snprintf(buf, sizeof(buf), "SIGNATURE %s", argv[0]); ++ return wpa_ctrl_command(ctrl, buf); ++} ++#endif /* CONFIG_TAXONOMY */ ++ ++ + #ifdef CONFIG_IEEE80211W + static int hostapd_cli_cmd_sa_query(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +@@ -667,8 +720,8 @@ + } + + +-static int wpa_ctrl_command_sta(struct wpa_ctrl *ctrl, char *cmd, +- char *addr, size_t addr_len) ++static int wpa_ctrl_command_sta(struct wpa_ctrl *ctrl, const char *cmd, ++ char *addr, size_t addr_len, int print) + { + char buf[4096], *pos; + size_t len; +@@ -692,7 +745,8 @@ + buf[len] = '\0'; + if (memcmp(buf, "FAIL", 4) == 0) + return -1; +- printf("%s", buf); ++ if (print) ++ printf("%s", buf); + + pos = buf; + while (*pos != '\0' && *pos != '\n') +@@ -708,27 +762,59 @@ + { + char addr[32], cmd[64]; + +- if (wpa_ctrl_command_sta(ctrl, "STA-FIRST", addr, sizeof(addr))) ++ if (wpa_ctrl_command_sta(ctrl, "STA-FIRST", addr, sizeof(addr), 1)) + return 0; + do { + snprintf(cmd, sizeof(cmd), "STA-NEXT %s", addr); +- } while (wpa_ctrl_command_sta(ctrl, cmd, addr, sizeof(addr)) == 0); ++ } while (wpa_ctrl_command_sta(ctrl, cmd, addr, sizeof(addr), 1) == 0); + + return -1; + } + + ++static int hostapd_cli_cmd_list_sta(struct wpa_ctrl *ctrl, int argc, ++ char *argv[]) ++{ ++ char addr[32], cmd[64]; ++ ++ if (wpa_ctrl_command_sta(ctrl, "STA-FIRST", addr, sizeof(addr), 0)) ++ return 0; ++ do { ++ if (os_strcmp(addr, "") != 0) ++ printf("%s\n", addr); ++ os_snprintf(cmd, sizeof(cmd), "STA-NEXT %s", addr); ++ } while (wpa_ctrl_command_sta(ctrl, cmd, addr, sizeof(addr), 0) == 0); ++ ++ return 0; ++} ++ ++ + static int hostapd_cli_cmd_help(struct wpa_ctrl *ctrl, int argc, char *argv[]) + { +- printf("%s", commands_help); ++ print_help(stdout, argc > 0 ? argv[0] : NULL); + return 0; + } + + ++static char ** hostapd_cli_complete_help(const char *str, int pos) ++{ ++ int arg = get_cmd_arg_num(str, pos); ++ char **res = NULL; ++ ++ switch (arg) { ++ case 1: ++ res = list_cmd_list(); ++ break; ++ } ++ ++ return res; ++} ++ ++ + static int hostapd_cli_cmd_license(struct wpa_ctrl *ctrl, int argc, + char *argv[]) + { +- printf("%s\n\n%s\n", hostapd_cli_version, hostapd_cli_full_license); ++ printf("%s\n\n%s\n", hostapd_cli_version, cli_full_license); + return 0; + } + +@@ -839,6 +925,47 @@ + } + + ++static void update_stations(struct wpa_ctrl *ctrl) ++{ ++ char addr[32], cmd[64]; ++ ++ if (!ctrl || !interactive) ++ return; ++ ++ cli_txt_list_flush(&stations); ++ ++ if (wpa_ctrl_command_sta(ctrl, "STA-FIRST", addr, sizeof(addr), 0)) ++ return; ++ do { ++ if (os_strcmp(addr, "") != 0) ++ cli_txt_list_add(&stations, addr); ++ os_snprintf(cmd, sizeof(cmd), "STA-NEXT %s", addr); ++ } while (wpa_ctrl_command_sta(ctrl, cmd, addr, sizeof(addr), 0) == 0); ++} ++ ++ ++static void hostapd_cli_get_interfaces(struct wpa_ctrl *ctrl, ++ struct dl_list *interfaces) ++{ ++ struct dirent *dent; ++ DIR *dir; ++ ++ if (!ctrl || !interfaces) ++ return; ++ dir = opendir(ctrl_iface_dir); ++ if (dir == NULL) ++ return; ++ ++ while ((dent = readdir(dir))) { ++ if (strcmp(dent->d_name, ".") == 0 || ++ strcmp(dent->d_name, "..") == 0) ++ continue; ++ cli_txt_list_add(interfaces, dent->d_name); ++ } ++ closedir(dir); ++} ++ ++ + static void hostapd_cli_list_interfaces(struct wpa_ctrl *ctrl) + { + struct dirent *dent; +@@ -869,22 +996,7 @@ + hostapd_cli_list_interfaces(ctrl); + return 0; + } +- +- hostapd_cli_close_connection(); +- os_free(ctrl_ifname); +- ctrl_ifname = os_strdup(argv[0]); +- if (ctrl_ifname == NULL) +- return -1; +- +- if (hostapd_cli_open_connection(ctrl_ifname)) { +- printf("Connected to interface '%s.\n", ctrl_ifname); +- if (wpa_ctrl_attach(ctrl_conn) == 0) { +- hostapd_cli_attached = 1; +- } else { +- printf("Warning: Failed to attach to " +- "hostapd.\n"); +- } +- } else { ++ if (hostapd_cli_reconnect(argv[0]) != 0) { + printf("Could not connect to interface '%s' - re-trying\n", + ctrl_ifname); + } +@@ -892,9 +1004,27 @@ + } + + ++static char ** hostapd_complete_interface(const char *str, int pos) ++{ ++ int arg = get_cmd_arg_num(str, pos); ++ char **res = NULL; ++ DEFINE_DL_LIST(interfaces); ++ ++ switch (arg) { ++ case 1: ++ hostapd_cli_get_interfaces(ctrl_conn, &interfaces); ++ res = cli_txt_list_array(&interfaces); ++ cli_txt_list_flush(&interfaces); ++ break; ++ } ++ ++ return res; ++} ++ ++ + static int hostapd_cli_cmd_set(struct wpa_ctrl *ctrl, int argc, char *argv[]) + { +- char cmd[256]; ++ char cmd[2048]; + int res; + + if (argc != 2) { +@@ -912,6 +1042,44 @@ + } + + ++static char ** hostapd_complete_set(const char *str, int pos) ++{ ++ int arg = get_cmd_arg_num(str, pos); ++ const char *fields[] = { ++#ifdef CONFIG_WPS_TESTING ++ "wps_version_number", "wps_testing_dummy_cred", ++ "wps_corrupt_pkhash", ++#endif /* CONFIG_WPS_TESTING */ ++#ifdef CONFIG_INTERWORKING ++ "gas_frag_limit", ++#endif /* CONFIG_INTERWORKING */ ++#ifdef CONFIG_TESTING_OPTIONS ++ "ext_mgmt_frame_handling", "ext_eapol_frame_io", ++#endif /* CONFIG_TESTING_OPTIONS */ ++#ifdef CONFIG_MBO ++ "mbo_assoc_disallow", ++#endif /* CONFIG_MBO */ ++ "deny_mac_file", "accept_mac_file", ++ }; ++ int i, num_fields = ARRAY_SIZE(fields); ++ ++ if (arg == 1) { ++ char **res; ++ ++ res = os_calloc(num_fields + 1, sizeof(char *)); ++ if (!res) ++ return NULL; ++ for (i = 0; i < num_fields; i++) { ++ res[i] = os_strdup(fields[i]); ++ if (!res[i]) ++ return res; ++ } ++ return res; ++ } ++ return NULL; ++} ++ ++ + static int hostapd_cli_cmd_get(struct wpa_ctrl *ctrl, int argc, char *argv[]) + { + char cmd[256]; +@@ -932,6 +1100,31 @@ + } + + ++static char ** hostapd_complete_get(const char *str, int pos) ++{ ++ int arg = get_cmd_arg_num(str, pos); ++ const char *fields[] = { ++ "version", "tls_library", ++ }; ++ int i, num_fields = ARRAY_SIZE(fields); ++ ++ if (arg == 1) { ++ char **res; ++ ++ res = os_calloc(num_fields + 1, sizeof(char *)); ++ if (!res) ++ return NULL; ++ for (i = 0; i < num_fields; i++) { ++ res[i] = os_strdup(fields[i]); ++ if (!res[i]) ++ return res; ++ } ++ return res; ++ } ++ return NULL; ++} ++ ++ + #ifdef CONFIG_FST + static int hostapd_cli_cmd_fst(struct wpa_ctrl *ctrl, int argc, char *argv[]) + { +@@ -1068,68 +1261,460 @@ + } + + ++static int hostapd_cli_cmd_raw(struct wpa_ctrl *ctrl, int argc, char *argv[]) ++{ ++ if (argc == 0) ++ return -1; ++ return hostapd_cli_cmd(ctrl, argv[0], 0, argc - 1, &argv[1]); ++} ++ ++ ++static int hostapd_cli_cmd_pmksa(struct wpa_ctrl *ctrl, int argc, char *argv[]) ++{ ++ return wpa_ctrl_command(ctrl, "PMKSA"); ++} ++ ++ ++static int hostapd_cli_cmd_pmksa_flush(struct wpa_ctrl *ctrl, int argc, ++ char *argv[]) ++{ ++ return wpa_ctrl_command(ctrl, "PMKSA_FLUSH"); ++} ++ ++ ++static int hostapd_cli_cmd_set_neighbor(struct wpa_ctrl *ctrl, int argc, ++ char *argv[]) ++{ ++ char cmd[2048]; ++ int res; ++ ++ if (argc < 3 || argc > 6) { ++ printf("Invalid set_neighbor command: needs 3-6 arguments\n"); ++ return -1; ++ } ++ ++ res = os_snprintf(cmd, sizeof(cmd), "SET_NEIGHBOR %s %s %s %s %s %s", ++ argv[0], argv[1], argv[2], argc >= 4 ? argv[3] : "", ++ argc >= 5 ? argv[4] : "", argc == 6 ? argv[5] : ""); ++ if (os_snprintf_error(sizeof(cmd), res)) { ++ printf("Too long SET_NEIGHBOR command.\n"); ++ return -1; ++ } ++ return wpa_ctrl_command(ctrl, cmd); ++} ++ ++ ++static int hostapd_cli_cmd_remove_neighbor(struct wpa_ctrl *ctrl, int argc, ++ char *argv[]) ++{ ++ char cmd[400]; ++ int res; ++ ++ if (argc != 2) { ++ printf("Invalid remove_neighbor command: needs 2 arguments\n"); ++ return -1; ++ } ++ ++ res = os_snprintf(cmd, sizeof(cmd), "REMOVE_NEIGHBOR %s %s", ++ argv[0], argv[1]); ++ if (os_snprintf_error(sizeof(cmd), res)) { ++ printf("Too long REMOVE_NEIGHBOR command.\n"); ++ return -1; ++ } ++ return wpa_ctrl_command(ctrl, cmd); ++} ++ ++ ++static int hostapd_cli_cmd_req_lci(struct wpa_ctrl *ctrl, int argc, ++ char *argv[]) ++{ ++ char cmd[256]; ++ int res; ++ ++ if (argc != 1) { ++ printf("Invalid req_lci command - requires destination address\n"); ++ return -1; ++ } ++ ++ res = os_snprintf(cmd, sizeof(cmd), "REQ_LCI %s", argv[0]); ++ if (os_snprintf_error(sizeof(cmd), res)) { ++ printf("Too long REQ_LCI command.\n"); ++ return -1; ++ } ++ return wpa_ctrl_command(ctrl, cmd); ++} ++ ++ ++static int hostapd_cli_cmd_req_range(struct wpa_ctrl *ctrl, int argc, ++ char *argv[]) ++{ ++ if (argc < 4) { ++ printf("Invalid req_range command: needs at least 4 arguments - dest address, randomization interval, min AP count, and 1 to 16 AP addresses\n"); ++ return -1; ++ } ++ ++ return hostapd_cli_cmd(ctrl, "REQ_RANGE", 4, argc, argv); ++} ++ ++ ++static int hostapd_cli_cmd_driver_flags(struct wpa_ctrl *ctrl, int argc, ++ char *argv[]) ++{ ++ return wpa_ctrl_command(ctrl, "DRIVER_FLAGS"); ++} ++ ++ ++#ifdef CONFIG_DPP ++ ++static int hostapd_cli_cmd_dpp_qr_code(struct wpa_ctrl *ctrl, int argc, ++ char *argv[]) ++{ ++ return hostapd_cli_cmd(ctrl, "DPP_QR_CODE", 1, argc, argv); ++} ++ ++ ++static int hostapd_cli_cmd_dpp_bootstrap_gen(struct wpa_ctrl *ctrl, int argc, ++ char *argv[]) ++{ ++ return hostapd_cli_cmd(ctrl, "DPP_BOOTSTRAP_GEN", 1, argc, argv); ++} ++ ++ ++static int hostapd_cli_cmd_dpp_bootstrap_remove(struct wpa_ctrl *ctrl, int argc, ++ char *argv[]) ++{ ++ return hostapd_cli_cmd(ctrl, "DPP_BOOTSTRAP_REMOVE", 1, argc, argv); ++} ++ ++ ++static int hostapd_cli_cmd_dpp_bootstrap_get_uri(struct wpa_ctrl *ctrl, ++ int argc, char *argv[]) ++{ ++ return hostapd_cli_cmd(ctrl, "DPP_BOOTSTRAP_GET_URI", 1, argc, argv); ++} ++ ++ ++static int hostapd_cli_cmd_dpp_bootstrap_info(struct wpa_ctrl *ctrl, int argc, ++ char *argv[]) ++{ ++ return hostapd_cli_cmd(ctrl, "DPP_BOOTSTRAP_INFO", 1, argc, argv); ++} ++ ++ ++static int hostapd_cli_cmd_dpp_auth_init(struct wpa_ctrl *ctrl, int argc, ++ char *argv[]) ++{ ++ return hostapd_cli_cmd(ctrl, "DPP_AUTH_INIT", 1, argc, argv); ++} ++ ++ ++static int hostapd_cli_cmd_dpp_listen(struct wpa_ctrl *ctrl, int argc, ++ char *argv[]) ++{ ++ return hostapd_cli_cmd(ctrl, "DPP_LISTEN", 1, argc, argv); ++} ++ ++ ++static int hostapd_cli_cmd_dpp_stop_listen(struct wpa_ctrl *ctrl, int argc, ++ char *argv[]) ++{ ++ return wpa_ctrl_command(ctrl, "DPP_STOP_LISTEN"); ++} ++ ++ ++static int hostapd_cli_cmd_dpp_configurator_add(struct wpa_ctrl *ctrl, int argc, ++ char *argv[]) ++{ ++ return hostapd_cli_cmd(ctrl, "DPP_CONFIGURATOR_ADD", 0, argc, argv); ++} ++ ++ ++static int hostapd_cli_cmd_dpp_configurator_remove(struct wpa_ctrl *ctrl, ++ int argc, char *argv[]) ++{ ++ return hostapd_cli_cmd(ctrl, "DPP_CONFIGURATOR_REMOVE", 1, argc, argv); ++} ++ ++ ++static int hostapd_cli_cmd_dpp_configurator_get_key(struct wpa_ctrl *ctrl, ++ int argc, char *argv[]) ++{ ++ return hostapd_cli_cmd(ctrl, "DPP_CONFIGURATOR_GET_KEY", 1, argc, argv); ++} ++ ++ ++static int hostapd_cli_cmd_dpp_configurator_sign(struct wpa_ctrl *ctrl, ++ int argc, char *argv[]) ++{ ++ return hostapd_cli_cmd(ctrl, "DPP_CONFIGURATOR_SIGN", 1, argc, argv); ++} ++ ++ ++static int hostapd_cli_cmd_dpp_pkex_add(struct wpa_ctrl *ctrl, int argc, ++ char *argv[]) ++{ ++ return hostapd_cli_cmd(ctrl, "DPP_PKEX_ADD", 1, argc, argv); ++} ++ ++ ++static int hostapd_cli_cmd_dpp_pkex_remove(struct wpa_ctrl *ctrl, int argc, ++ char *argv[]) ++{ ++ return hostapd_cli_cmd(ctrl, "DPP_PKEX_REMOVE", 1, argc, argv); ++} ++ ++#endif /* CONFIG_DPP */ ++ ++ ++static int hostapd_cli_cmd_accept_macacl(struct wpa_ctrl *ctrl, int argc, ++ char *argv[]) ++{ ++ return hostapd_cli_cmd(ctrl, "ACCEPT_ACL", 1, argc, argv); ++} ++ ++ ++static int hostapd_cli_cmd_deny_macacl(struct wpa_ctrl *ctrl, int argc, ++ char *argv[]) ++{ ++ return hostapd_cli_cmd(ctrl, "DENY_ACL", 1, argc, argv); ++} ++ ++ ++static int hostapd_cli_cmd_poll_sta(struct wpa_ctrl *ctrl, int argc, ++ char *argv[]) ++{ ++ return hostapd_cli_cmd(ctrl, "POLL_STA", 1, argc, argv); ++} ++ ++ ++static int hostapd_cli_cmd_req_beacon(struct wpa_ctrl *ctrl, int argc, ++ char *argv[]) ++{ ++ return hostapd_cli_cmd(ctrl, "REQ_BEACON", 2, argc, argv); ++} ++ ++ ++static int hostapd_cli_cmd_reload_wpa_psk(struct wpa_ctrl *ctrl, int argc, ++ char *argv[]) ++{ ++ return wpa_ctrl_command(ctrl, "RELOAD_WPA_PSK"); ++} ++ ++ + struct hostapd_cli_cmd { + const char *cmd; + int (*handler)(struct wpa_ctrl *ctrl, int argc, char *argv[]); ++ char ** (*completion)(const char *str, int pos); ++ const char *usage; + }; + + static const struct hostapd_cli_cmd hostapd_cli_commands[] = { +- { "ping", hostapd_cli_cmd_ping }, +- { "mib", hostapd_cli_cmd_mib }, +- { "relog", hostapd_cli_cmd_relog }, +- { "status", hostapd_cli_cmd_status }, +- { "sta", hostapd_cli_cmd_sta }, +- { "all_sta", hostapd_cli_cmd_all_sta }, +- { "new_sta", hostapd_cli_cmd_new_sta }, +- { "deauthenticate", hostapd_cli_cmd_deauthenticate }, +- { "disassociate", hostapd_cli_cmd_disassociate }, ++ { "ping", hostapd_cli_cmd_ping, NULL, ++ "= pings hostapd" }, ++ { "mib", hostapd_cli_cmd_mib, NULL, ++ "= get MIB variables (dot1x, dot11, radius)" }, ++ { "relog", hostapd_cli_cmd_relog, NULL, ++ "= reload/truncate debug log output file" }, ++ { "status", hostapd_cli_cmd_status, NULL, ++ "= show interface status info" }, ++ { "sta", hostapd_cli_cmd_sta, hostapd_complete_stations, ++ " = get MIB variables for one station" }, ++ { "all_sta", hostapd_cli_cmd_all_sta, NULL, ++ "= get MIB variables for all stations" }, ++ { "list_sta", hostapd_cli_cmd_list_sta, NULL, ++ "= list all stations" }, ++ { "new_sta", hostapd_cli_cmd_new_sta, NULL, ++ " = add a new station" }, ++ { "deauthenticate", hostapd_cli_cmd_deauthenticate, ++ hostapd_complete_stations, ++ " = deauthenticate a station" }, ++ { "disassociate", hostapd_cli_cmd_disassociate, ++ hostapd_complete_stations, ++ " = disassociate a station" }, ++#ifdef CONFIG_TAXONOMY ++ { "signature", hostapd_cli_cmd_signature, hostapd_complete_stations, ++ " = get taxonomy signature for a station" }, ++#endif /* CONFIG_TAXONOMY */ + #ifdef CONFIG_IEEE80211W +- { "sa_query", hostapd_cli_cmd_sa_query }, ++ { "sa_query", hostapd_cli_cmd_sa_query, hostapd_complete_stations, ++ " = send SA Query to a station" }, + #endif /* CONFIG_IEEE80211W */ + #ifdef CONFIG_WPS +- { "wps_pin", hostapd_cli_cmd_wps_pin }, +- { "wps_check_pin", hostapd_cli_cmd_wps_check_pin }, +- { "wps_pbc", hostapd_cli_cmd_wps_pbc }, +- { "wps_cancel", hostapd_cli_cmd_wps_cancel }, ++ { "wps_pin", hostapd_cli_cmd_wps_pin, NULL, ++ " [timeout] [addr] = add WPS Enrollee PIN" }, ++ { "wps_check_pin", hostapd_cli_cmd_wps_check_pin, NULL, ++ " = verify PIN checksum" }, ++ { "wps_pbc", hostapd_cli_cmd_wps_pbc, NULL, ++ "= indicate button pushed to initiate PBC" }, ++ { "wps_cancel", hostapd_cli_cmd_wps_cancel, NULL, ++ "= cancel the pending WPS operation" }, + #ifdef CONFIG_WPS_NFC +- { "wps_nfc_tag_read", hostapd_cli_cmd_wps_nfc_tag_read }, +- { "wps_nfc_config_token", hostapd_cli_cmd_wps_nfc_config_token }, +- { "wps_nfc_token", hostapd_cli_cmd_wps_nfc_token }, +- { "nfc_get_handover_sel", hostapd_cli_cmd_nfc_get_handover_sel }, ++ { "wps_nfc_tag_read", hostapd_cli_cmd_wps_nfc_tag_read, NULL, ++ " = report read NFC tag with WPS data" }, ++ { "wps_nfc_config_token", hostapd_cli_cmd_wps_nfc_config_token, NULL, ++ " = build NFC configuration token" }, ++ { "wps_nfc_token", hostapd_cli_cmd_wps_nfc_token, NULL, ++ " = manager NFC password token" }, ++ { "nfc_get_handover_sel", hostapd_cli_cmd_nfc_get_handover_sel, NULL, ++ NULL }, + #endif /* CONFIG_WPS_NFC */ +- { "wps_ap_pin", hostapd_cli_cmd_wps_ap_pin }, +- { "wps_config", hostapd_cli_cmd_wps_config }, +- { "wps_get_status", hostapd_cli_cmd_wps_get_status }, ++ { "wps_ap_pin", hostapd_cli_cmd_wps_ap_pin, NULL, ++ " [params..] = enable/disable AP PIN" }, ++ { "wps_config", hostapd_cli_cmd_wps_config, NULL, ++ " = configure AP" }, ++ { "wps_get_status", hostapd_cli_cmd_wps_get_status, NULL, ++ "= show current WPS status" }, + #endif /* CONFIG_WPS */ +- { "disassoc_imminent", hostapd_cli_cmd_disassoc_imminent }, +- { "ess_disassoc", hostapd_cli_cmd_ess_disassoc }, +- { "bss_tm_req", hostapd_cli_cmd_bss_tm_req }, +- { "get_config", hostapd_cli_cmd_get_config }, +- { "help", hostapd_cli_cmd_help }, +- { "interface", hostapd_cli_cmd_interface }, ++ { "disassoc_imminent", hostapd_cli_cmd_disassoc_imminent, NULL, ++ "= send Disassociation Imminent notification" }, ++ { "ess_disassoc", hostapd_cli_cmd_ess_disassoc, NULL, ++ "= send ESS Dissassociation Imminent notification" }, ++ { "bss_tm_req", hostapd_cli_cmd_bss_tm_req, NULL, ++ "= send BSS Transition Management Request" }, ++ { "get_config", hostapd_cli_cmd_get_config, NULL, ++ "= show current configuration" }, ++ { "help", hostapd_cli_cmd_help, hostapd_cli_complete_help, ++ "= show this usage help" }, ++ { "interface", hostapd_cli_cmd_interface, hostapd_complete_interface, ++ "[ifname] = show interfaces/select interface" }, + #ifdef CONFIG_FST +- { "fst", hostapd_cli_cmd_fst }, ++ { "fst", hostapd_cli_cmd_fst, NULL, ++ " = send FST-MANAGER control interface command" }, + #endif /* CONFIG_FST */ +- { "level", hostapd_cli_cmd_level }, +- { "license", hostapd_cli_cmd_license }, +- { "quit", hostapd_cli_cmd_quit }, +- { "set", hostapd_cli_cmd_set }, +- { "get", hostapd_cli_cmd_get }, +- { "set_qos_map_set", hostapd_cli_cmd_set_qos_map_set }, +- { "send_qos_map_conf", hostapd_cli_cmd_send_qos_map_conf }, +- { "chan_switch", hostapd_cli_cmd_chan_switch }, +- { "hs20_wnm_notif", hostapd_cli_cmd_hs20_wnm_notif }, +- { "hs20_deauth_req", hostapd_cli_cmd_hs20_deauth_req }, +- { "vendor", hostapd_cli_cmd_vendor }, +- { "enable", hostapd_cli_cmd_enable }, +- { "reload", hostapd_cli_cmd_reload }, +- { "disable", hostapd_cli_cmd_disable }, +- { "erp_flush", hostapd_cli_cmd_erp_flush }, +- { "log_level", hostapd_cli_cmd_log_level }, +- { NULL, NULL } ++ { "raw", hostapd_cli_cmd_raw, NULL, ++ " = send unprocessed command" }, ++ { "level", hostapd_cli_cmd_level, NULL, ++ " = change debug level" }, ++ { "license", hostapd_cli_cmd_license, NULL, ++ "= show full hostapd_cli license" }, ++ { "quit", hostapd_cli_cmd_quit, NULL, ++ "= exit hostapd_cli" }, ++ { "set", hostapd_cli_cmd_set, hostapd_complete_set, ++ " = set runtime variables" }, ++ { "get", hostapd_cli_cmd_get, hostapd_complete_get, ++ " = get runtime info" }, ++ { "set_qos_map_set", hostapd_cli_cmd_set_qos_map_set, NULL, ++ " = set QoS Map set element" }, ++ { "send_qos_map_conf", hostapd_cli_cmd_send_qos_map_conf, ++ hostapd_complete_stations, ++ " = send QoS Map Configure frame" }, ++ { "chan_switch", hostapd_cli_cmd_chan_switch, NULL, ++ " [sec_channel_offset=] [center_freq1=]\n" ++ " [center_freq2=] [bandwidth=] [blocktx] [ht|vht]\n" ++ " = initiate channel switch announcement" }, ++ { "hs20_wnm_notif", hostapd_cli_cmd_hs20_wnm_notif, NULL, ++ " \n" ++ " = send WNM-Notification Subscription Remediation Request" }, ++ { "hs20_deauth_req", hostapd_cli_cmd_hs20_deauth_req, NULL, ++ " [url]\n" ++ " = send WNM-Notification imminent deauthentication indication" }, ++ { "vendor", hostapd_cli_cmd_vendor, NULL, ++ " []\n" ++ " = send vendor driver command" }, ++ { "enable", hostapd_cli_cmd_enable, NULL, ++ "= enable hostapd on current interface" }, ++ { "reload", hostapd_cli_cmd_reload, NULL, ++ "= reload configuration for current interface" }, ++ { "disable", hostapd_cli_cmd_disable, NULL, ++ "= disable hostapd on current interface" }, ++ { "erp_flush", hostapd_cli_cmd_erp_flush, NULL, ++ "= drop all ERP keys"}, ++ { "log_level", hostapd_cli_cmd_log_level, NULL, ++ "[level] = show/change log verbosity level" }, ++ { "pmksa", hostapd_cli_cmd_pmksa, NULL, ++ " = show PMKSA cache entries" }, ++ { "pmksa_flush", hostapd_cli_cmd_pmksa_flush, NULL, ++ " = flush PMKSA cache" }, ++ { "set_neighbor", hostapd_cli_cmd_set_neighbor, NULL, ++ " [lci=] [civic=] [stat]\n" ++ " = add AP to neighbor database" }, ++ { "remove_neighbor", hostapd_cli_cmd_remove_neighbor, NULL, ++ " = remove AP from neighbor database" }, ++ { "req_lci", hostapd_cli_cmd_req_lci, hostapd_complete_stations, ++ " = send LCI request to a station"}, ++ { "req_range", hostapd_cli_cmd_req_range, NULL, ++ " = send FTM range request"}, ++ { "driver_flags", hostapd_cli_cmd_driver_flags, NULL, ++ " = show supported driver flags"}, ++#ifdef CONFIG_DPP ++ { "dpp_qr_code", hostapd_cli_cmd_dpp_qr_code, NULL, ++ "report a scanned DPP URI from a QR Code" }, ++ { "dpp_bootstrap_gen", hostapd_cli_cmd_dpp_bootstrap_gen, NULL, ++ "type= [chan=..] [mac=..] [info=..] [curve=..] [key=..] = generate DPP bootstrap information" }, ++ { "dpp_bootstrap_remove", hostapd_cli_cmd_dpp_bootstrap_remove, NULL, ++ "*| = remove DPP bootstrap information" }, ++ { "dpp_bootstrap_get_uri", hostapd_cli_cmd_dpp_bootstrap_get_uri, NULL, ++ " = get DPP bootstrap URI" }, ++ { "dpp_bootstrap_info", hostapd_cli_cmd_dpp_bootstrap_info, NULL, ++ " = show DPP bootstrap information" }, ++ { "dpp_auth_init", hostapd_cli_cmd_dpp_auth_init, NULL, ++ "peer= [own=] = initiate DPP bootstrapping" }, ++ { "dpp_listen", hostapd_cli_cmd_dpp_listen, NULL, ++ " = start DPP listen" }, ++ { "dpp_stop_listen", hostapd_cli_cmd_dpp_stop_listen, NULL, ++ "= stop DPP listen" }, ++ { "dpp_configurator_add", hostapd_cli_cmd_dpp_configurator_add, NULL, ++ "[curve=..] [key=..] = add DPP configurator" }, ++ { "dpp_configurator_remove", hostapd_cli_cmd_dpp_configurator_remove, ++ NULL, ++ "*| = remove DPP configurator" }, ++ { "dpp_configurator_get_key", hostapd_cli_cmd_dpp_configurator_get_key, ++ NULL, ++ " = Get DPP configurator's private key" }, ++ { "dpp_configurator_sign", hostapd_cli_cmd_dpp_configurator_sign, NULL, ++ "conf= configurator= = generate self DPP configuration" }, ++ { "dpp_pkex_add", hostapd_cli_cmd_dpp_pkex_add, NULL, ++ "add PKEX code" }, ++ { "dpp_pkex_remove", hostapd_cli_cmd_dpp_pkex_remove, NULL, ++ "*| = remove DPP pkex information" }, ++#endif /* CONFIG_DPP */ ++ { "accept_acl", hostapd_cli_cmd_accept_macacl, NULL, ++ "=Add/Delete/Show/Clear accept MAC ACL" }, ++ { "deny_acl", hostapd_cli_cmd_deny_macacl, NULL, ++ "=Add/Delete/Show/Clear deny MAC ACL" }, ++ { "poll_sta", hostapd_cli_cmd_poll_sta, hostapd_complete_stations, ++ " = poll a STA to check connectivity with a QoS null frame" }, ++ { "req_beacon", hostapd_cli_cmd_req_beacon, NULL, ++ " [req_mode=] = send a Beacon report request to a station" }, ++ { "reload_wpa_psk", hostapd_cli_cmd_reload_wpa_psk, NULL, ++ "= reload wpa_psk_file only" }, ++ { NULL, NULL, NULL, NULL } + }; + + ++/* ++ * Prints command usage, lines are padded with the specified string. ++ */ ++static void print_cmd_help(FILE *stream, const struct hostapd_cli_cmd *cmd, ++ const char *pad) ++{ ++ char c; ++ size_t n; ++ ++ if (cmd->usage == NULL) ++ return; ++ fprintf(stream, "%s%s ", pad, cmd->cmd); ++ for (n = 0; (c = cmd->usage[n]); n++) { ++ fprintf(stream, "%c", c); ++ if (c == '\n') ++ fprintf(stream, "%s", pad); ++ } ++ fprintf(stream, "\n"); ++} ++ ++ ++static void print_help(FILE *stream, const char *cmd) ++{ ++ int n; ++ ++ fprintf(stream, "commands:\n"); ++ for (n = 0; hostapd_cli_commands[n].cmd; n++) { ++ if (cmd == NULL || str_starts(hostapd_cli_commands[n].cmd, cmd)) ++ print_cmd_help(stream, &hostapd_cli_commands[n], " "); ++ } ++} ++ ++ + static void wpa_request(struct wpa_ctrl *ctrl, int argc, char *argv[]) + { + const struct hostapd_cli_cmd *cmd, *match = NULL; +@@ -1169,6 +1754,34 @@ + } + + ++static void cli_event(const char *str) ++{ ++ const char *start, *s; ++ ++ start = os_strchr(str, '>'); ++ if (start == NULL) ++ return; ++ ++ start++; ++ ++ if (str_starts(start, AP_STA_CONNECTED)) { ++ s = os_strchr(start, ' '); ++ if (s == NULL) ++ return; ++ cli_txt_list_add(&stations, s + 1); ++ return; ++ } ++ ++ if (str_starts(start, AP_STA_DISCONNECTED)) { ++ s = os_strchr(start, ' '); ++ if (s == NULL) ++ return; ++ cli_txt_list_del_addr(&stations, s + 1); ++ return; ++ } ++} ++ ++ + static void hostapd_cli_recv_pending(struct wpa_ctrl *ctrl, int in_read, + int action_monitor) + { +@@ -1176,7 +1789,7 @@ + if (ctrl_conn == NULL) + return; + while (wpa_ctrl_pending(ctrl)) { +- char buf[256]; ++ char buf[4096]; + size_t len = sizeof(buf) - 1; + if (wpa_ctrl_recv(ctrl, buf, &len) == 0) { + buf[len] = '\0'; +@@ -1183,6 +1796,7 @@ + if (action_monitor) + hostapd_cli_action_process(buf, len); + else { ++ cli_event(buf); + if (in_read && first) + printf("\n"); + first = 0; +@@ -1196,35 +1810,9 @@ + } + + +-#define max_args 10 +- +-static int tokenize_cmd(char *cmd, char *argv[]) ++static void hostapd_cli_receive(int sock, void *eloop_ctx, void *sock_ctx) + { +- char *pos; +- int argc = 0; +- +- pos = cmd; +- for (;;) { +- while (*pos == ' ') +- pos++; +- if (*pos == '\0') +- break; +- argv[argc] = pos; +- argc++; +- if (argc == max_args) +- break; +- if (*pos == '"') { +- char *pos2 = os_strrchr(pos, '"'); +- if (pos2) +- pos = pos2 + 1; +- } +- while (*pos != '\0' && *pos != ' ') +- pos++; +- if (*pos == ' ') +- *pos++ = '\0'; +- } +- +- return argc; ++ hostapd_cli_recv_pending(ctrl_conn, 0, 0); + } + + +@@ -1234,18 +1822,8 @@ + printf("Connection to hostapd lost - trying to reconnect\n"); + hostapd_cli_close_connection(); + } +- if (!ctrl_conn) { +- ctrl_conn = hostapd_cli_open_connection(ctrl_ifname); +- if (ctrl_conn) { +- printf("Connection to hostapd re-established\n"); +- if (wpa_ctrl_attach(ctrl_conn) == 0) { +- hostapd_cli_attached = 1; +- } else { +- printf("Warning: Failed to attach to " +- "hostapd.\n"); +- } +- } +- } ++ if (!ctrl_conn && hostapd_cli_reconnect(ctrl_ifname) == 0) ++ printf("Connection to hostapd re-established\n"); + if (ctrl_conn) + hostapd_cli_recv_pending(ctrl_conn, 1, 0); + eloop_register_timeout(ping_interval, 0, hostapd_cli_ping, NULL, NULL); +@@ -1274,18 +1852,100 @@ + } + + ++static char ** list_cmd_list(void) ++{ ++ char **res; ++ int i, count; ++ ++ count = ARRAY_SIZE(hostapd_cli_commands); ++ res = os_calloc(count + 1, sizeof(char *)); ++ if (res == NULL) ++ return NULL; ++ ++ for (i = 0; hostapd_cli_commands[i].cmd; i++) { ++ res[i] = os_strdup(hostapd_cli_commands[i].cmd); ++ if (res[i] == NULL) ++ break; ++ } ++ ++ return res; ++} ++ ++ ++static char ** hostapd_cli_cmd_completion(const char *cmd, const char *str, ++ int pos) ++{ ++ int i; ++ ++ for (i = 0; hostapd_cli_commands[i].cmd; i++) { ++ if (os_strcasecmp(hostapd_cli_commands[i].cmd, cmd) != 0) ++ continue; ++ if (hostapd_cli_commands[i].completion) ++ return hostapd_cli_commands[i].completion(str, pos); ++ if (!hostapd_cli_commands[i].usage) ++ return NULL; ++ edit_clear_line(); ++ printf("\r%s\n", hostapd_cli_commands[i].usage); ++ edit_redraw(); ++ break; ++ } ++ ++ return NULL; ++} ++ ++ ++static char ** hostapd_cli_edit_completion_cb(void *ctx, const char *str, ++ int pos) ++{ ++ char **res; ++ const char *end; ++ char *cmd; ++ ++ end = os_strchr(str, ' '); ++ if (end == NULL || str + pos < end) ++ return list_cmd_list(); ++ ++ cmd = os_malloc(pos + 1); ++ if (cmd == NULL) ++ return NULL; ++ os_memcpy(cmd, str, pos); ++ cmd[end - str] = '\0'; ++ res = hostapd_cli_cmd_completion(cmd, str, pos); ++ os_free(cmd); ++ return res; ++} ++ ++ + static void hostapd_cli_interactive(void) + { ++ char *hfile = NULL; ++ char *home; ++ + printf("\nInteractive mode\n\n"); + ++#ifdef CONFIG_HOSTAPD_CLI_HISTORY_DIR ++ home = CONFIG_HOSTAPD_CLI_HISTORY_DIR; ++#else /* CONFIG_HOSTAPD_CLI_HISTORY_DIR */ ++ home = getenv("HOME"); ++#endif /* CONFIG_HOSTAPD_CLI_HISTORY_DIR */ ++ if (home) { ++ const char *fname = ".hostapd_cli_history"; ++ int hfile_len = os_strlen(home) + 1 + os_strlen(fname) + 1; ++ hfile = os_malloc(hfile_len); ++ if (hfile) ++ os_snprintf(hfile, hfile_len, "%s/%s", home, fname); ++ } ++ + eloop_register_signal_terminate(hostapd_cli_eloop_terminate, NULL); + edit_init(hostapd_cli_edit_cmd_cb, hostapd_cli_edit_eof_cb, +- NULL, NULL, NULL, NULL); ++ hostapd_cli_edit_completion_cb, NULL, hfile, NULL); + eloop_register_timeout(ping_interval, 0, hostapd_cli_ping, NULL, NULL); + + eloop_run(); + +- edit_deinit(NULL, NULL); ++ cli_txt_list_flush(&stations); ++ edit_deinit(hfile, NULL); ++ os_free(hfile); + eloop_cancel_timeout(hostapd_cli_ping, NULL, NULL); + } + +@@ -1388,8 +2048,7 @@ + interactive = (argc == optind) && (action_file == NULL); + + if (interactive) { +- printf("%s\n\n%s\n\n", hostapd_cli_version, +- hostapd_cli_license); ++ printf("%s\n\n%s\n\n", hostapd_cli_version, cli_license); + } + + if (eloop_init()) +@@ -1413,7 +2072,7 @@ + closedir(dir); + } + } +- ctrl_conn = hostapd_cli_open_connection(ctrl_ifname); ++ hostapd_cli_reconnect(ctrl_ifname); + if (ctrl_conn) { + if (warning_displayed) + printf("Connection established.\n"); +@@ -1434,18 +2093,10 @@ + continue; + } + +- if (interactive || action_file) { +- if (wpa_ctrl_attach(ctrl_conn) == 0) { +- hostapd_cli_attached = 1; +- } else { +- printf("Warning: Failed to attach to hostapd.\n"); +- if (action_file) +- return -1; +- } +- } +- +- if (daemonize && os_daemonize(pid_file)) ++ if (action_file && !hostapd_cli_attached) + return -1; ++ if (daemonize && os_daemonize(pid_file) && eloop_sock_requeue()) ++ return -1; + + if (interactive) + hostapd_cli_interactive(); +@@ -1454,8 +2105,18 @@ + else + wpa_request(ctrl_conn, argc - optind, &argv[optind]); + ++ unregister_event_handler(ctrl_conn); + os_free(ctrl_ifname); + eloop_destroy(); + hostapd_cli_cleanup(); + return 0; + } ++ ++#else /* CONFIG_NO_CTRL_IFACE */ ++ ++int main(int argc, char *argv[]) ++{ ++ return -1; ++} ++ ++#endif /* CONFIG_NO_CTRL_IFACE */ +--- contrib/wpa/hostapd/main.c.orig ++++ contrib/wpa/hostapd/main.c +@@ -1,6 +1,6 @@ + /* + * hostapd / main() +- * Copyright (c) 2002-2015, Jouni Malinen ++ * Copyright (c) 2002-2019, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. +@@ -18,6 +18,7 @@ + #include "crypto/random.h" + #include "crypto/tls.h" + #include "common/version.h" ++#include "common/dpp.h" + #include "drivers/driver.h" + #include "eap_server/eap.h" + #include "eap_server/tncs.h" +@@ -24,6 +25,7 @@ + #include "ap/hostapd.h" + #include "ap/ap_config.h" + #include "ap/ap_drv_ops.h" ++#include "ap/dpp_hostapd.h" + #include "fst/fst.h" + #include "config_file.h" + #include "eap_register.h" +@@ -108,6 +110,10 @@ + module_str ? module_str : "", + module_str ? ": " : "", txt); + ++#ifdef CONFIG_DEBUG_SYSLOG ++ if (wpa_debug_syslog) ++ conf_stdout = 0; ++#endif /* CONFIG_DEBUG_SYSLOG */ + if ((conf_stdout & module) && level >= conf_stdout_level) { + wpa_debug_print_timestamp(); + wpa_printf(MSG_INFO, "%s", format); +@@ -171,7 +177,8 @@ + + if (global.drv_priv[i] == NULL && + wpa_drivers[i]->global_init) { +- global.drv_priv[i] = wpa_drivers[i]->global_init(); ++ global.drv_priv[i] = ++ wpa_drivers[i]->global_init(iface->interfaces); + if (global.drv_priv[i] == NULL) { + wpa_printf(MSG_ERROR, "Failed to initialize " + "driver '%s'", +@@ -216,11 +223,20 @@ + iface->drv_flags = capa.flags; + iface->smps_modes = capa.smps_modes; + iface->probe_resp_offloads = capa.probe_resp_offloads; ++ /* ++ * Use default extended capa values from per-radio information ++ */ + iface->extended_capa = capa.extended_capa; + iface->extended_capa_mask = capa.extended_capa_mask; + iface->extended_capa_len = capa.extended_capa_len; + iface->drv_max_acl_mac_addrs = capa.max_acl_mac_addrs; + ++ /* ++ * Override extended capa with per-interface type (AP), if ++ * available from the driver. ++ */ ++ hostapd_get_ext_capa(iface); ++ + triggs = wpa_get_wowlan_triggers(conf->wowlan_triggers, &capa); + if (triggs && hapd->driver->set_wowlan) { + if (hapd->driver->set_wowlan(hapd->drv_priv, triggs)) +@@ -238,10 +254,10 @@ + * + * This function is used to parse configuration file for a full interface (one + * or more BSSes sharing the same radio) and allocate memory for the BSS +- * interfaces. No actiual driver operations are started. ++ * interfaces. No actual driver operations are started. + */ + static struct hostapd_iface * +-hostapd_interface_init(struct hapd_interfaces *interfaces, ++hostapd_interface_init(struct hapd_interfaces *interfaces, const char *if_name, + const char *config_fname, int debug) + { + struct hostapd_iface *iface; +@@ -251,6 +267,12 @@ + iface = hostapd_init(interfaces, config_fname); + if (!iface) + return NULL; ++ ++ if (if_name) { ++ os_strlcpy(iface->conf->bss[0]->iface, if_name, ++ sizeof(iface->conf->bss[0]->iface)); ++ } ++ + iface->interfaces = interfaces; + + for (k = 0; k < debug; k++) { +@@ -260,7 +282,8 @@ + + if (iface->conf->bss[0]->iface[0] == '\0' && + !hostapd_drv_none(iface->bss[0])) { +- wpa_printf(MSG_ERROR, "Interface name not specified in %s", ++ wpa_printf(MSG_ERROR, ++ "Interface name not specified in %s, nor by '-i' parameter", + config_fname); + hostapd_interface_deinit_free(iface); + return NULL; +@@ -329,6 +352,7 @@ + wpa_printf(MSG_ERROR, "Failed to initialize event loop"); + return -1; + } ++ interfaces->eloop_initialized = 1; + + random_init(entropy_file); + +@@ -356,7 +380,7 @@ + } + + +-static void hostapd_global_deinit(const char *pid_file) ++static void hostapd_global_deinit(const char *pid_file, int eloop_initialized) + { + int i; + +@@ -374,7 +398,8 @@ + + random_deinit(); + +- eloop_destroy(); ++ if (eloop_initialized) ++ eloop_destroy(); + + #ifndef CONFIG_NATIVE_WINDOWS + closelog(); +@@ -408,9 +433,16 @@ + } + #endif /* EAP_SERVER_TNC */ + +- if (daemonize && os_daemonize(pid_file)) { +- wpa_printf(MSG_ERROR, "daemon: %s", strerror(errno)); +- return -1; ++ if (daemonize) { ++ if (os_daemonize(pid_file)) { ++ wpa_printf(MSG_ERROR, "daemon: %s", strerror(errno)); ++ return -1; ++ } ++ if (eloop_sock_requeue()) { ++ wpa_printf(MSG_ERROR, "eloop_sock_requeue: %s", ++ strerror(errno)); ++ return -1; ++ } + } + + eloop_run(); +@@ -425,7 +457,7 @@ + "hostapd v" VERSION_STR "\n" + "User space daemon for IEEE 802.11 AP management,\n" + "IEEE 802.1X/WPA/WPA2/EAP/RADIUS Authenticator\n" +- "Copyright (c) 2002-2015, Jouni Malinen " ++ "Copyright (c) 2002-2019, Jouni Malinen " + "and contributors\n"); + } + +@@ -437,7 +469,8 @@ + "\n" + "usage: hostapd [-hdBKtv] [-P ] [-e ] " + "\\\n" +- " [-g ] [-G ] \\\n" ++ " [-g ] [-G ]\\\n" ++ " [-i ]\\\n" + " \n" + "\n" + "options:\n" +@@ -453,9 +486,14 @@ + " -f log output to debug file instead of stdout\n" + #endif /* CONFIG_DEBUG_FILE */ + #ifdef CONFIG_DEBUG_LINUX_TRACING +- " -T = record to Linux tracing in addition to logging\n" ++ " -T record to Linux tracing in addition to logging\n" + " (records all messages regardless of debug verbosity)\n" + #endif /* CONFIG_DEBUG_LINUX_TRACING */ ++ " -i list of interface names to use\n" ++#ifdef CONFIG_DEBUG_SYSLOG ++ " -s log output to syslog instead of stdout\n" ++#endif /* CONFIG_DEBUG_SYSLOG */ ++ " -S start all the interfaces synchronously\n" + " -t include timestamps in some debug messages\n" + " -v show hostapd version\n"); + +@@ -466,9 +504,8 @@ + static const char * hostapd_msg_ifname_cb(void *ctx) + { + struct hostapd_data *hapd = ctx; +- if (hapd && hapd->iconf && hapd->iconf->bss && +- hapd->iconf->num_bss > 0 && hapd->iconf->bss[0]) +- return hapd->iconf->bss[0]->iface; ++ if (hapd && hapd->conf) ++ return hapd->conf->iface; + return NULL; + } + +@@ -476,11 +513,16 @@ + static int hostapd_get_global_ctrl_iface(struct hapd_interfaces *interfaces, + const char *path) + { ++#ifndef CONFIG_CTRL_IFACE_UDP + char *pos; ++#endif /* !CONFIG_CTRL_IFACE_UDP */ ++ + os_free(interfaces->global_iface_path); + interfaces->global_iface_path = os_strdup(path); + if (interfaces->global_iface_path == NULL) + return -1; ++ ++#ifndef CONFIG_CTRL_IFACE_UDP + pos = os_strrchr(interfaces->global_iface_path, '/'); + if (pos == NULL) { + wpa_printf(MSG_ERROR, "No '/' in the global control interface " +@@ -492,6 +534,7 @@ + + *pos = '\0'; + interfaces->global_iface_name = pos + 1; ++#endif /* !CONFIG_CTRL_IFACE_UDP */ + + return 0; + } +@@ -513,6 +556,43 @@ + } + + ++static int hostapd_get_interface_names(char ***if_names, ++ size_t *if_names_size, ++ char *arg) ++{ ++ char *if_name, *tmp, **nnames; ++ size_t i; ++ ++ if (!arg) ++ return -1; ++ if_name = strtok_r(arg, ",", &tmp); ++ ++ while (if_name) { ++ nnames = os_realloc_array(*if_names, 1 + *if_names_size, ++ sizeof(char *)); ++ if (!nnames) ++ goto fail; ++ *if_names = nnames; ++ ++ (*if_names)[*if_names_size] = os_strdup(if_name); ++ if (!(*if_names)[*if_names_size]) ++ goto fail; ++ (*if_names_size)++; ++ if_name = strtok_r(NULL, ",", &tmp); ++ } ++ ++ return 0; ++ ++fail: ++ for (i = 0; i < *if_names_size; i++) ++ os_free((*if_names)[i]); ++ os_free(*if_names); ++ *if_names = NULL; ++ *if_names_size = 0; ++ return -1; ++} ++ ++ + #ifdef CONFIG_WPS + static int gen_uuid(const char *txt_addr) + { +@@ -570,6 +650,9 @@ + #ifdef CONFIG_DEBUG_LINUX_TRACING + int enable_trace_dbg = 0; + #endif /* CONFIG_DEBUG_LINUX_TRACING */ ++ int start_ifaces_in_sync = 0; ++ char **if_names = NULL; ++ size_t if_names_size = 0; + + if (os_program_init()) + return -1; +@@ -584,10 +667,18 @@ + interfaces.global_iface_path = NULL; + interfaces.global_iface_name = NULL; + interfaces.global_ctrl_sock = -1; +- interfaces.global_ctrl_dst = NULL; ++ dl_list_init(&interfaces.global_ctrl_dst); ++#ifdef CONFIG_ETH_P_OUI ++ dl_list_init(&interfaces.eth_p_oui); ++#endif /* CONFIG_ETH_P_OUI */ ++#ifdef CONFIG_DPP ++ interfaces.dpp = dpp_global_init(); ++ if (!interfaces.dpp) ++ return -1; ++#endif /* CONFIG_DPP */ + + for (;;) { +- c = getopt(argc, argv, "b:Bde:f:hKP:Ttu:vg:G:"); ++ c = getopt(argc, argv, "b:Bde:f:hi:KP:sSTtu:vg:G:"); + if (c < 0) + break; + switch (c) { +@@ -644,10 +735,23 @@ + bss_config = tmp_bss; + bss_config[num_bss_configs++] = optarg; + break; ++#ifdef CONFIG_DEBUG_SYSLOG ++ case 's': ++ wpa_debug_syslog = 1; ++ break; ++#endif /* CONFIG_DEBUG_SYSLOG */ ++ case 'S': ++ start_ifaces_in_sync = 1; ++ break; + #ifdef CONFIG_WPS + case 'u': + return gen_uuid(optarg); + #endif /* CONFIG_WPS */ ++ case 'i': ++ if (hostapd_get_interface_names(&if_names, ++ &if_names_size, optarg)) ++ goto out; ++ break; + default: + usage(); + break; +@@ -664,6 +768,10 @@ + wpa_debug_open_file(log_file); + else + wpa_debug_setup_stdout(); ++#ifdef CONFIG_DEBUG_SYSLOG ++ if (wpa_debug_syslog) ++ wpa_debug_open_syslog(); ++#endif /* CONFIG_DEBUG_SYSLOG */ + #ifdef CONFIG_DEBUG_LINUX_TRACING + if (enable_trace_dbg) { + int tret = wpa_debug_open_linux_tracing(); +@@ -705,7 +813,13 @@ + + /* Allocate and parse configuration for full interface files */ + for (i = 0; i < interfaces.count; i++) { ++ char *if_name = NULL; ++ ++ if (i < if_names_size) ++ if_name = if_names[i]; ++ + interfaces.iface[i] = hostapd_interface_init(&interfaces, ++ if_name, + argv[optind + i], + debug); + if (!interfaces.iface[i]) { +@@ -712,6 +826,8 @@ + wpa_printf(MSG_ERROR, "Failed to initialize interface"); + goto out; + } ++ if (start_ifaces_in_sync) ++ interfaces.iface[i]->need_to_start_in_sync = 1; + } + + /* Allocate and parse configuration for per-BSS files */ +@@ -787,10 +903,16 @@ + } + os_free(interfaces.iface); + +- eloop_cancel_timeout(hostapd_periodic, &interfaces, NULL); +- hostapd_global_deinit(pid_file); ++#ifdef CONFIG_DPP ++ dpp_global_deinit(interfaces.dpp); ++#endif /* CONFIG_DPP */ ++ ++ if (interfaces.eloop_initialized) ++ eloop_cancel_timeout(hostapd_periodic, &interfaces, NULL); ++ hostapd_global_deinit(pid_file, interfaces.eloop_initialized); + os_free(pid_file); + ++ wpa_debug_close_syslog(); + if (log_file) + wpa_debug_close_file(); + wpa_debug_close_linux_tracing(); +@@ -797,6 +919,10 @@ + + os_free(bss_config); + ++ for (i = 0; i < if_names_size; i++) ++ os_free(if_names[i]); ++ os_free(if_names); ++ + fst_global_deinit(); + + os_program_deinit(); +--- contrib/wpa/hostapd/wps-ap-nfc.py.orig ++++ contrib/wpa/hostapd/wps-ap-nfc.py +@@ -26,7 +26,7 @@ + success_file = None + + def summary(txt): +- print txt ++ print(txt) + if summary_file: + with open(summary_file, 'a') as f: + f.write(txt + "\n") +@@ -42,12 +42,12 @@ + if os.path.isdir(wpas_ctrl): + try: + ifaces = [os.path.join(wpas_ctrl, i) for i in os.listdir(wpas_ctrl)] +- except OSError, error: +- print "Could not find hostapd: ", error ++ except OSError as error: ++ print("Could not find hostapd: ", error) + return None + + if len(ifaces) < 1: +- print "No hostapd control interface found" ++ print("No hostapd control interface found") + return None + + for ctrl in ifaces: +@@ -54,7 +54,7 @@ + try: + wpas = wpaspy.Ctrl(ctrl) + return wpas +- except Exception, e: ++ except Exception as e: + pass + return None + +@@ -133,15 +133,15 @@ + def process_request(self, request): + summary("HandoverServer - request received") + try: +- print "Parsed handover request: " + request.pretty() +- except Exception, e: +- print e +- print str(request).encode("hex") ++ print("Parsed handover request: " + request.pretty()) ++ except Exception as e: ++ print(e) ++ print(str(request).encode("hex")) + + sel = nfc.ndef.HandoverSelectMessage(version="1.2") + + for carrier in request.carriers: +- print "Remote carrier type: " + carrier.type ++ print("Remote carrier type: " + carrier.type) + if carrier.type == "application/vnd.wfa.wsc": + summary("WPS carrier type match - add WPS carrier record") + data = wpas_get_handover_sel() +@@ -148,8 +148,8 @@ + if data is None: + summary("Could not get handover select carrier record from hostapd") + continue +- print "Handover select carrier record from hostapd:" +- print data.encode("hex") ++ print("Handover select carrier record from hostapd:") ++ print(data.encode("hex")) + if "OK" in wpas_report_handover(carrier.record, data): + success_report("Handover reported successfully") + else: +@@ -158,12 +158,12 @@ + message = nfc.ndef.Message(data); + sel.add_carrier(message[0], "active", message[1:]) + +- print "Handover select:" ++ print("Handover select:") + try: +- print sel.pretty() +- except Exception, e: +- print e +- print str(sel).encode("hex") ++ print(sel.pretty()) ++ except Exception as e: ++ print(e) ++ print(str(sel).encode("hex")) + + summary("Sending handover select") + self.success = True +@@ -174,7 +174,7 @@ + success = False + if len(tag.ndef.message): + for record in tag.ndef.message: +- print "record type " + record.type ++ print("record type " + record.type) + if record.type == "application/vnd.wfa.wsc": + summary("WPS tag - send to hostapd") + success = wpas_tag_read(tag.ndef.message) +@@ -193,7 +193,7 @@ + global write_data + tag.ndef.message = str(write_data) + success_report("Tag write succeeded") +- print "Done - remove tag" ++ print("Done - remove tag") + global only_one + if only_one: + global continue_loop +@@ -211,7 +211,7 @@ + summary("Could not get WPS config token from hostapd") + return + +- print "Touch an NFC tag" ++ print("Touch an NFC tag") + clf.connect(rdwr={'on-connect': rdwr_connected_write}) + + +@@ -224,7 +224,7 @@ + summary("Could not get WPS password token from hostapd") + return + +- print "Touch an NFC tag" ++ print("Touch an NFC tag") + clf.connect(rdwr={'on-connect': rdwr_connected_write}) + + +@@ -233,11 +233,11 @@ + summary("Tag connected: " + str(tag)) + + if tag.ndef: +- print "NDEF tag: " + tag.type ++ print("NDEF tag: " + tag.type) + try: +- print tag.ndef.message.pretty() +- except Exception, e: +- print e ++ print(tag.ndef.message.pretty()) ++ except Exception as e: ++ print(e) + success = wps_tag_read(tag) + if only_one and success: + global continue_loop +@@ -250,13 +250,13 @@ + + + def llcp_startup(clf, llc): +- print "Start LLCP server" ++ print("Start LLCP server") + global srv + srv = HandoverServer(llc) + return llc + + def llcp_connected(llc): +- print "P2P LLCP connected" ++ print("P2P LLCP connected") + global wait_connection + wait_connection = False + global srv +@@ -304,7 +304,7 @@ + + try: + if not clf.open("usb"): +- print "Could not open connection with an NFC device" ++ print("Could not open connection with an NFC device") + raise SystemExit + + if args.command == "write-config": +@@ -317,7 +317,7 @@ + + global continue_loop + while continue_loop: +- print "Waiting for a tag or peer to be touched" ++ print("Waiting for a tag or peer to be touched") + wait_connection = True + try: + if not clf.connect(rdwr={'on-connect': rdwr_connected}, +@@ -324,8 +324,8 @@ + llcp={'on-startup': llcp_startup, + 'on-connect': llcp_connected}): + break +- except Exception, e: +- print "clf.connect failed" ++ except Exception as e: ++ print("clf.connect failed") + + global srv + if only_one and srv and srv.success: +--- contrib/wpa/hs20/client/Android.mk.orig ++++ contrib/wpa/hs20/client/Android.mk +@@ -4,7 +4,6 @@ + INCLUDES += $(LOCAL_PATH)/../../src/utils + INCLUDES += $(LOCAL_PATH)/../../src/common + INCLUDES += $(LOCAL_PATH)/../../src +-INCLUDES += external/openssl/include + INCLUDES += external/libxml2/include + INCLUDES += external/curl/include + INCLUDES += external/webkit/Source/WebKit/gtk +@@ -55,6 +54,7 @@ + OBJS += ../../src/crypto/md5-internal.c + OBJS += ../../src/crypto/sha1-internal.c + OBJS += ../../src/crypto/sha256-internal.c ++OBJS += ../../src/crypto/tls_openssl_ocsp.c + + L_CFLAGS += -DEAP_TLS_OPENSSL + +--- contrib/wpa/hs20/client/Makefile.orig ++++ contrib/wpa/hs20/client/Makefile +@@ -8,12 +8,17 @@ + LDO=$(CC) + endif + ++ifeq ($(QUIET), 1) + Q=@ ++E=true ++else ++Q=@ + E=echo + ifeq ($(V), 1) + Q= + E=true + endif ++endif + + ifndef CFLAGS + CFLAGS = -MMD -O2 -Wall -g +@@ -76,6 +81,7 @@ + endif + + CFLAGS += -DEAP_TLS_OPENSSL ++OBJS += ../../src/crypto/tls_openssl_ocsp.o + LIBS += -lssl -lcrypto + + hs20-osu-client: $(OBJS) +--- contrib/wpa/hs20/client/est.c.orig ++++ contrib/wpa/hs20/client/est.c +@@ -16,6 +16,10 @@ + #include + #include + #include ++#include ++#ifdef OPENSSL_IS_BORINGSSL ++#include ++#endif /* OPENSSL_IS_BORINGSSL */ + + #include "common.h" + #include "utils/base64.h" +@@ -27,12 +31,28 @@ + static int pkcs7_to_cert(struct hs20_osu_client *ctx, const u8 *pkcs7, + size_t len, char *pem_file, char *der_file) + { ++#ifdef OPENSSL_IS_BORINGSSL ++ CBS pkcs7_cbs; ++#else /* OPENSSL_IS_BORINGSSL */ + PKCS7 *p7 = NULL; + const unsigned char *p = pkcs7; ++#endif /* OPENSSL_IS_BORINGSSL */ + STACK_OF(X509) *certs; + int i, num, ret = -1; + BIO *out = NULL; + ++#ifdef OPENSSL_IS_BORINGSSL ++ certs = sk_X509_new_null(); ++ if (!certs) ++ goto fail; ++ CBS_init(&pkcs7_cbs, pkcs7, len); ++ if (!PKCS7_get_certificates(certs, &pkcs7_cbs)) { ++ wpa_printf(MSG_INFO, "Could not parse PKCS#7 object: %s", ++ ERR_error_string(ERR_get_error(), NULL)); ++ write_result(ctx, "Could not parse PKCS#7 object from EST"); ++ goto fail; ++ } ++#else /* OPENSSL_IS_BORINGSSL */ + p7 = d2i_PKCS7(NULL, &p, len); + if (p7 == NULL) { + wpa_printf(MSG_INFO, "Could not parse PKCS#7 object: %s", +@@ -52,6 +72,7 @@ + certs = NULL; + break; + } ++#endif /* OPENSSL_IS_BORINGSSL */ + + if (!certs || ((num = sk_X509_num(certs)) == 0)) { + wpa_printf(MSG_INFO, "No certificates found in PKCS#7 object"); +@@ -84,7 +105,12 @@ + ret = 0; + + fail: ++#ifdef OPENSSL_IS_BORINGSSL ++ if (certs) ++ sk_X509_pop_free(certs, X509_free); ++#else /* OPENSSL_IS_BORINGSSL */ + PKCS7_free(p7); ++#endif /* OPENSSL_IS_BORINGSSL */ + if (out) + BIO_free_all(out); + +@@ -194,6 +220,10 @@ + } d; + } AttrOrOID; + ++#if OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(OPENSSL_IS_BORINGSSL) ++DEFINE_STACK_OF(AttrOrOID) ++#endif ++ + typedef struct { + int type; + STACK_OF(AttrOrOID) *attrs; +@@ -310,9 +340,34 @@ + if (!csrattrs || ! csrattrs->attrs) + return; + ++#ifdef OPENSSL_IS_BORINGSSL ++ num = sk_num(CHECKED_CAST(_STACK *, STACK_OF(AttrOrOID) *, ++ csrattrs->attrs)); ++ for (i = 0; i < num; i++) { ++ AttrOrOID *ao = sk_value( ++ CHECKED_CAST(_STACK *, const STACK_OF(AttrOrOID) *, ++ csrattrs->attrs), i); ++ switch (ao->type) { ++ case 0: ++ add_csrattrs_oid(ctx, ao->d.oid, exts); ++ break; ++ case 1: ++ add_csrattrs_attr(ctx, ao->d.attribute, exts); ++ break; ++ } ++ } ++#else /* OPENSSL_IS_BORINGSSL */ ++#if OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(OPENSSL_IS_BORINGSSL) ++ num = sk_AttrOrOID_num(csrattrs->attrs); ++#else + num = SKM_sk_num(AttrOrOID, csrattrs->attrs); ++#endif + for (i = 0; i < num; i++) { ++#if OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(OPENSSL_IS_BORINGSSL) ++ AttrOrOID *ao = sk_AttrOrOID_value(csrattrs->attrs, i); ++#else + AttrOrOID *ao = SKM_sk_value(AttrOrOID, csrattrs->attrs, i); ++#endif + switch (ao->type) { + case 0: + add_csrattrs_oid(ctx, ao->d.oid, exts); +@@ -322,6 +377,7 @@ + break; + } + } ++#endif /* OPENSSL_IS_BORINGSSL */ + } + + +@@ -340,6 +396,7 @@ + STACK_OF(X509_EXTENSION) *exts = NULL; + X509_EXTENSION *ex; + BIO *out; ++ CONF *ctmp = NULL; + + wpa_printf(MSG_INFO, "Generate RSA private key"); + write_summary(ctx, "Generate RSA private key"); +@@ -421,20 +478,20 @@ + if (!exts) + goto fail; + +- ex = X509V3_EXT_conf_nid(NULL, NULL, NID_basic_constraints, +- "CA:FALSE"); ++ ex = X509V3_EXT_nconf_nid(ctmp, NULL, NID_basic_constraints, ++ "CA:FALSE"); + if (ex == NULL || + !sk_X509_EXTENSION_push(exts, ex)) + goto fail; + +- ex = X509V3_EXT_conf_nid(NULL, NULL, NID_key_usage, +- "nonRepudiation,digitalSignature,keyEncipherment"); ++ ex = X509V3_EXT_nconf_nid(ctmp, NULL, NID_key_usage, ++ "nonRepudiation,digitalSignature,keyEncipherment"); + if (ex == NULL || + !sk_X509_EXTENSION_push(exts, ex)) + goto fail; + +- ex = X509V3_EXT_conf_nid(NULL, NULL, NID_ext_key_usage, +- "1.3.6.1.4.1.40808.1.1.2"); ++ ex = X509V3_EXT_nconf_nid(ctmp, NULL, NID_ext_key_usage, ++ "1.3.6.1.4.1.40808.1.1.2"); + if (ex == NULL || + !sk_X509_EXTENSION_push(exts, ex)) + goto fail; +@@ -454,7 +511,9 @@ + char *txt; + size_t rlen; + ++#if !defined(ANDROID) || !defined(OPENSSL_IS_BORINGSSL) + X509_REQ_print(out, req); ++#endif + rlen = BIO_ctrl_pending(out); + txt = os_malloc(rlen + 1); + if (txt) { +@@ -473,7 +532,9 @@ + FILE *f = fopen(csr_pem, "w"); + if (f == NULL) + goto fail; ++#if !defined(ANDROID) || !defined(OPENSSL_IS_BORINGSSL) + X509_REQ_print_fp(f, req); ++#endif + if (!PEM_write_X509_REQ(f, req)) { + fclose(f); + goto fail; +@@ -618,7 +679,6 @@ + char *buf, *resp, *req, *req2; + size_t buflen, resp_len, len, pkcs7_len; + unsigned char *pkcs7; +- FILE *f; + char client_cert_buf[200]; + char client_key_buf[200]; + const char *client_cert = NULL, *client_key = NULL; +@@ -673,11 +733,6 @@ + return -1; + } + wpa_printf(MSG_DEBUG, "EST simpleenroll response: %s", resp); +- f = fopen("Cert/est-resp.raw", "w"); +- if (f) { +- fwrite(resp, resp_len, 1, f); +- fclose(f); +- } + + pkcs7 = base64_decode((unsigned char *) resp, resp_len, &pkcs7_len); + if (pkcs7 == NULL) { +--- contrib/wpa/hs20/client/oma_dm_client.c.orig ++++ contrib/wpa/hs20/client/oma_dm_client.c +@@ -111,6 +111,12 @@ + xml_node_t *syncml, *synchdr; + xml_namespace_t *ns; + ++ if (!ctx->devid) { ++ wpa_printf(MSG_ERROR, ++ "DevId from devinfo.xml is not available - cannot use OMA DM"); ++ return NULL; ++ } ++ + syncml = xml_node_create_root(ctx->xml, "SYNCML:SYNCML1.2", NULL, &ns, + "SyncML"); + +--- contrib/wpa/hs20/client/osu_client.c.orig ++++ contrib/wpa/hs20/client/osu_client.c +@@ -105,6 +105,35 @@ + } + + ++static int android_update_permission(const char *path, mode_t mode) ++{ ++#ifdef ANDROID ++ /* we need to change file/folder permission for Android */ ++ ++ if (!path) { ++ wpa_printf(MSG_ERROR, "file path null"); ++ return -1; ++ } ++ ++ /* Allow processes running with Group ID as AID_WIFI, ++ * to read files from SP, SP/, Cert and osu-info directories */ ++ if (lchown(path, -1, AID_WIFI)) { ++ wpa_printf(MSG_INFO, "CTRL: Could not lchown directory: %s", ++ strerror(errno)); ++ return -1; ++ } ++ ++ if (chmod(path, mode) < 0) { ++ wpa_printf(MSG_INFO, "CTRL: Could not chmod directory: %s", ++ strerror(errno)); ++ return -1; ++ } ++#endif /* ANDROID */ ++ ++ return 0; ++} ++ ++ + int osu_get_certificate(struct hs20_osu_client *ctx, xml_node_t *getcert) + { + xml_node_t *node; +@@ -169,6 +198,8 @@ + } + + mkdir("Cert", S_IRWXU); ++ android_update_permission("Cert", S_IRWXU | S_IRWXG); ++ + if (est_load_cacerts(ctx, url) < 0 || + est_build_csr(ctx, url) < 0 || + est_simple_enroll(ctx, url, user, pw) < 0) +@@ -262,7 +293,6 @@ + + unlink("Cert/est-req.b64"); + unlink("Cert/est-req.pem"); +- unlink("Cert/est-resp.raw"); + rmdir("Cert"); + + return 0; +@@ -406,7 +436,7 @@ + if (node == NULL) { + wpa_printf(MSG_INFO, "No Policy/PolicyUpdate/TrustRoot/CertURL found from PPS"); + xml_node_free(ctx->xml, pps); +- return -1; ++ return -2; + } + + ret = download_cert(ctx, node, ca_fname); +@@ -433,7 +463,7 @@ + if (node == NULL) { + wpa_printf(MSG_INFO, "No AAAServerTrustRoot/CertURL found from PPS"); + xml_node_free(ctx->xml, pps); +- return -1; ++ return -2; + } + + aaa = xml_node_first_child(ctx->xml, node); +@@ -455,7 +485,7 @@ + { + char *dir, *pos; + char fname[300]; +- int ret; ++ int ret, ret1; + + dir = os_strdup(pps_fname); + if (dir == NULL) +@@ -470,9 +500,13 @@ + snprintf(fname, sizeof(fname), "%s/ca.pem", dir); + ret = cmd_dl_osu_ca(ctx, pps_fname, fname); + snprintf(fname, sizeof(fname), "%s/polupd-ca.pem", dir); +- cmd_dl_polupd_ca(ctx, pps_fname, fname); ++ ret1 = cmd_dl_polupd_ca(ctx, pps_fname, fname); ++ if (ret == 0 && ret1 == -1) ++ ret = -1; + snprintf(fname, sizeof(fname), "%s/aaa-ca.pem", dir); +- cmd_dl_aaa_ca(ctx, pps_fname, fname); ++ ret1 = cmd_dl_aaa_ca(ctx, pps_fname, fname); ++ if (ret == 0 && ret1 == -1) ++ ret = -1; + + os_free(dir); + +@@ -578,20 +612,8 @@ + } + } + +-#ifdef ANDROID +- /* Allow processes running with Group ID as AID_WIFI, +- * to read files from SP/ directory */ +- if (chown(fname, -1, AID_WIFI)) { +- wpa_printf(MSG_INFO, "CTRL: Could not chown directory: %s", +- strerror(errno)); +- /* Try to continue anyway */ +- } +- if (chmod(fname, S_IRWXU | S_IRGRP | S_IXGRP) < 0) { +- wpa_printf(MSG_INFO, "CTRL: Could not chmod directory: %s", +- strerror(errno)); +- /* Try to continue anyway */ +- } +-#endif /* ANDROID */ ++ android_update_permission("SP", S_IRWXU | S_IRWXG); ++ android_update_permission(fname, S_IRWXU | S_IRWXG); + + snprintf(fname, fname_len, "SP/%s/pps.xml", fqdn); + +@@ -1213,8 +1235,7 @@ + homeoi) < 0) + wpa_printf(MSG_INFO, "Failed to set cred required_roaming_consortium"); + } else { +- if (set_cred_quoted(ctx->ifname, id, "roaming_consortium", +- homeoi) < 0) ++ if (set_cred(ctx->ifname, id, "roaming_consortium", homeoi) < 0) + wpa_printf(MSG_INFO, "Failed to set cred roaming_consortium"); + } + +@@ -1289,7 +1310,9 @@ + if (str == NULL) + return; + wpa_printf(MSG_INFO, "- HomeSP/RoamingConsortiumOI = %s", str); +- /* TODO: Set to wpa_supplicant */ ++ if (set_cred_quoted(ctx->ifname, id, "roaming_consortiums", ++ str) < 0) ++ wpa_printf(MSG_INFO, "Failed to set cred roaming_consortiums"); + xml_node_get_text_free(ctx->xml, str); + } + +@@ -1442,10 +1465,92 @@ + } + + ++static void set_pps_cred_eap_method_eap_type(struct hs20_osu_client *ctx, ++ int id, xml_node_t *node) ++{ ++ char *str = xml_node_get_text(ctx->xml, node); ++ int type; ++ const char *eap_method = NULL; ++ ++ if (!str) ++ return; ++ wpa_printf(MSG_INFO, ++ "- Credential/UsernamePassword/EAPMethod/EAPType = %s", str); ++ type = atoi(str); ++ switch (type) { ++ case EAP_TYPE_TLS: ++ eap_method = "TLS"; ++ break; ++ case EAP_TYPE_TTLS: ++ eap_method = "TTLS"; ++ break; ++ case EAP_TYPE_PEAP: ++ eap_method = "PEAP"; ++ break; ++ case EAP_TYPE_PWD: ++ eap_method = "PWD"; ++ break; ++ } ++ xml_node_get_text_free(ctx->xml, str); ++ if (!eap_method) { ++ wpa_printf(MSG_INFO, "Unknown EAPType value"); ++ return; ++ } ++ ++ if (set_cred(ctx->ifname, id, "eap", eap_method) < 0) ++ wpa_printf(MSG_INFO, "Failed to set cred eap"); ++} ++ ++ ++static void set_pps_cred_eap_method_inner_method(struct hs20_osu_client *ctx, ++ int id, xml_node_t *node) ++{ ++ char *str = xml_node_get_text(ctx->xml, node); ++ const char *phase2 = NULL; ++ ++ if (!str) ++ return; ++ wpa_printf(MSG_INFO, ++ "- Credential/UsernamePassword/EAPMethod/InnerMethod = %s", ++ str); ++ if (os_strcmp(str, "PAP") == 0) ++ phase2 = "auth=PAP"; ++ else if (os_strcmp(str, "CHAP") == 0) ++ phase2 = "auth=CHAP"; ++ else if (os_strcmp(str, "MS-CHAP") == 0) ++ phase2 = "auth=MSCHAP"; ++ else if (os_strcmp(str, "MS-CHAP-V2") == 0) ++ phase2 = "auth=MSCHAPV2"; ++ xml_node_get_text_free(ctx->xml, str); ++ if (!phase2) { ++ wpa_printf(MSG_INFO, "Unknown InnerMethod value"); ++ return; ++ } ++ ++ if (set_cred_quoted(ctx->ifname, id, "phase2", phase2) < 0) ++ wpa_printf(MSG_INFO, "Failed to set cred phase2"); ++} ++ ++ + static void set_pps_cred_eap_method(struct hs20_osu_client *ctx, int id, + xml_node_t *node) + { +- wpa_printf(MSG_INFO, "- Credential/UsernamePassword/EAPMethod - TODO"); ++ xml_node_t *child; ++ const char *name; ++ ++ wpa_printf(MSG_INFO, "- Credential/UsernamePassword/EAPMethod"); ++ ++ xml_node_for_each_child(ctx->xml, child, node) { ++ xml_node_for_each_check(ctx->xml, child); ++ name = xml_node_get_localname(ctx->xml, child); ++ if (os_strcasecmp(name, "EAPType") == 0) ++ set_pps_cred_eap_method_eap_type(ctx, id, child); ++ else if (os_strcasecmp(name, "InnerMethod") == 0) ++ set_pps_cred_eap_method_inner_method(ctx, id, child); ++ else ++ wpa_printf(MSG_INFO, "Unknown Credential/UsernamePassword/EAPMethod node '%s'", ++ name); ++ } + } + + +@@ -1884,7 +1989,9 @@ + char url[256]; + unsigned int methods; + char osu_ssid[33]; ++ char osu_ssid2[33]; + char osu_nai[256]; ++ char osu_nai2[256]; + struct osu_lang_text friendly_name[MAX_OSU_VALS]; + size_t friendly_name_count; + struct osu_lang_text serv_desc[MAX_OSU_VALS]; +@@ -1943,6 +2050,12 @@ + continue; + } + ++ if (strncmp(buf, "osu_ssid2=", 10) == 0) { ++ snprintf(last->osu_ssid2, sizeof(last->osu_ssid2), ++ "%s", buf + 10); ++ continue; ++ } ++ + if (os_strncmp(buf, "osu_nai=", 8) == 0) { + os_snprintf(last->osu_nai, sizeof(last->osu_nai), + "%s", buf + 8); +@@ -1949,6 +2062,12 @@ + continue; + } + ++ if (os_strncmp(buf, "osu_nai2=", 9) == 0) { ++ os_snprintf(last->osu_nai2, sizeof(last->osu_nai2), ++ "%s", buf + 9); ++ continue; ++ } ++ + if (strncmp(buf, "friendly_name=", 14) == 0) { + struct osu_lang_text *txt; + if (last->friendly_name_count == MAX_OSU_VALS) +@@ -2024,9 +2143,9 @@ + + + static int osu_connect(struct hs20_osu_client *ctx, const char *bssid, +- const char *ssid, const char *url, ++ const char *ssid, const char *ssid2, const char *url, + unsigned int methods, int no_prod_assoc, +- const char *osu_nai) ++ const char *osu_nai, const char *osu_nai2) + { + int id; + const char *ifname = ctx->ifname; +@@ -2034,11 +2153,32 @@ + struct wpa_ctrl *mon; + int res; + ++ if (ssid2 && ssid2[0] == '\0') ++ ssid2 = NULL; ++ ++ if (ctx->osu_ssid) { ++ if (os_strcmp(ssid, ctx->osu_ssid) == 0) { ++ wpa_printf(MSG_DEBUG, ++ "Enforced OSU SSID matches ANQP info"); ++ ssid2 = NULL; ++ } else if (ssid2 && os_strcmp(ssid2, ctx->osu_ssid) == 0) { ++ wpa_printf(MSG_DEBUG, ++ "Enforced OSU SSID matches RSN[OSEN] info"); ++ ssid = ssid2; ++ } else { ++ wpa_printf(MSG_INFO, "Enforced OSU SSID did not match"); ++ write_summary(ctx, "Enforced OSU SSID did not match"); ++ return -1; ++ } ++ } ++ + id = add_network(ifname); + if (id < 0) + return -1; + if (set_network_quoted(ifname, id, "ssid", ssid) < 0) + return -1; ++ if (ssid2) ++ osu_nai = osu_nai2; + if (osu_nai && os_strlen(osu_nai) > 0) { + char dir[255], fname[300]; + if (getcwd(dir, sizeof(dir)) == NULL) +@@ -2045,15 +2185,22 @@ + return -1; + os_snprintf(fname, sizeof(fname), "%s/osu-ca.pem", dir); + ++ if (ssid2 && set_network_quoted(ifname, id, "ssid", ssid2) < 0) ++ return -1; ++ + if (set_network(ifname, id, "proto", "OSEN") < 0 || + set_network(ifname, id, "key_mgmt", "OSEN") < 0 || + set_network(ifname, id, "pairwise", "CCMP") < 0 || +- set_network(ifname, id, "group", "GTK_NOT_USED") < 0 || ++ set_network(ifname, id, "group", "GTK_NOT_USED CCMP") < 0 || + set_network(ifname, id, "eap", "WFA-UNAUTH-TLS") < 0 || + set_network(ifname, id, "ocsp", "2") < 0 || + set_network_quoted(ifname, id, "identity", osu_nai) < 0 || + set_network_quoted(ifname, id, "ca_cert", fname) < 0) + return -1; ++ } else if (ssid2) { ++ wpa_printf(MSG_INFO, "No OSU_NAI set for RSN[OSEN]"); ++ write_summary(ctx, "No OSU_NAI set for RSN[OSEN]"); ++ return -1; + } else { + if (set_network(ifname, id, "key_mgmt", "NONE") < 0) + return -1; +@@ -2134,7 +2281,7 @@ + char fname[255]; + FILE *f; + struct osu_data *osu = NULL, *last = NULL; +- size_t osu_count, i, j; ++ size_t osu_count = 0, i, j; + int ret; + + write_summary(ctx, "OSU provider selection"); +@@ -2229,8 +2376,12 @@ + fprintf(f, "
    BSSID: %s
    \n" + "SSID: %s
    \n", + last->bssid, last->osu_ssid); +- if (last->osu_nai) ++ if (last->osu_ssid2[0]) ++ fprintf(f, "SSID2: %s
    \n", last->osu_ssid2); ++ if (last->osu_nai[0]) + fprintf(f, "NAI: %s
    \n", last->osu_nai); ++ if (last->osu_nai2[0]) ++ fprintf(f, "NAI2: %s
    \n", last->osu_nai2); + fprintf(f, "URL: %s
    \n" + "methods:%s%s
    \n" + "

    \n", +@@ -2257,6 +2408,8 @@ + ret = 0; + wpa_printf(MSG_INFO, "BSSID: %s", last->bssid); + wpa_printf(MSG_INFO, "SSID: %s", last->osu_ssid); ++ if (last->osu_ssid2[0]) ++ wpa_printf(MSG_INFO, "SSID2: %s", last->osu_ssid2); + wpa_printf(MSG_INFO, "URL: %s", last->url); + write_summary(ctx, "Selected OSU provider id=%d BSSID=%s SSID=%s URL=%s", + ret, last->bssid, last->osu_ssid, last->url); +@@ -2311,10 +2464,13 @@ + "No supported OSU provisioning method"); + ret = -1; + } +- } else if (connect) ++ } else if (connect) { + ret = osu_connect(ctx, last->bssid, last->osu_ssid, ++ last->osu_ssid2, + last->url, last->methods, +- no_prod_assoc, last->osu_nai); ++ no_prod_assoc, last->osu_nai, ++ last->osu_nai2); ++ } + } else + ret = -1; + +@@ -2339,12 +2495,15 @@ + return -1; + + snprintf(fname, sizeof(fname), "%s/osu-info", dir); +- if (mkdir(fname, S_IRWXU | S_IRWXG) < 0 && errno != EEXIST) { ++ if (mkdir(fname, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) < 0 && ++ errno != EEXIST) { + wpa_printf(MSG_INFO, "mkdir(%s) failed: %s", + fname, strerror(errno)); + return -1; + } + ++ android_update_permission(fname, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH); ++ + snprintf(buf, sizeof(buf), "SET osu_dir %s", fname); + if (wpa_command(ifname, buf) < 0) { + wpa_printf(MSG_INFO, "Failed to configure osu_dir to wpa_supplicant"); +@@ -2559,7 +2718,7 @@ + if (!pps_fname) { + char buf[256]; + wpa_printf(MSG_INFO, "Determining PPS file based on Home SP information"); +- if (os_strncmp(address, "fqdn=", 5) == 0) { ++ if (address && os_strncmp(address, "fqdn=", 5) == 0) { + wpa_printf(MSG_INFO, "Use requested FQDN from command line"); + os_snprintf(buf, sizeof(buf), "%s", address + 5); + address = NULL; +@@ -2909,26 +3068,19 @@ + return -1; + + devinfo = node_from_file(ctx->xml, "devinfo.xml"); +- if (!devinfo) { +- wpa_printf(MSG_ERROR, "devinfo.xml not found"); +- return -1; +- } ++ if (devinfo) { ++ devid = get_node(ctx->xml, devinfo, "DevId"); ++ if (devid) { ++ char *tmp = xml_node_get_text(ctx->xml, devid); + +- devid = get_node(ctx->xml, devinfo, "DevId"); +- if (devid) { +- char *tmp = xml_node_get_text(ctx->xml, devid); +- if (tmp) { +- ctx->devid = os_strdup(tmp); +- xml_node_get_text_free(ctx->xml, tmp); ++ if (tmp) { ++ ctx->devid = os_strdup(tmp); ++ xml_node_get_text_free(ctx->xml, tmp); ++ } + } ++ xml_node_free(ctx->xml, devinfo); + } +- xml_node_free(ctx->xml, devinfo); + +- if (ctx->devid == NULL) { +- wpa_printf(MSG_ERROR, "Could not fetch DevId from devinfo.xml"); +- return -1; +- } +- + ctx->http = http_init_ctx(ctx, ctx->xml); + if (ctx->http == NULL) { + xml_node_deinit_ctx(ctx->xml); +@@ -3029,7 +3181,7 @@ + return -1; + + for (;;) { +- c = getopt(argc, argv, "df:hKNO:qr:s:S:tw:x:"); ++ c = getopt(argc, argv, "df:hKNo:O:qr:s:S:tw:x:"); + if (c < 0) + break; + switch (c) { +@@ -3046,6 +3198,9 @@ + case 'N': + no_prod_assoc = 1; + break; ++ case 'o': ++ ctx.osu_ssid = optarg; ++ break; + case 'O': + friendly_name = optarg; + break; +@@ -3122,20 +3277,12 @@ + usage(); + exit(0); + } +- if (argc - optind < 2) +- wpa_printf(MSG_ERROR, "Server URL missing from command line"); +- else +- ret = cmd_sub_rem(&ctx, argv[optind + 1], +- argc > optind + 2 ? +- argv[optind + 2] : NULL, +- argc > optind + 3 ? +- argv[optind + 3] : NULL); ++ ret = cmd_sub_rem(&ctx, argv[optind + 1], ++ argc > optind + 2 ? argv[optind + 2] : NULL, ++ argc > optind + 3 ? argv[optind + 3] : NULL); + } else if (strcmp(argv[optind], "pol_upd") == 0) { +- if (argc - optind < 2) { +- usage(); +- exit(0); +- } +- ret = cmd_pol_upd(&ctx, argc > 2 ? argv[optind + 1] : NULL, ++ ret = cmd_pol_upd(&ctx, ++ argc > optind + 1 ? argv[optind + 1] : NULL, + argc > optind + 2 ? argv[optind + 2] : NULL, + argc > optind + 3 ? argv[optind + 3] : NULL); + } else if (strcmp(argv[optind], "prov") == 0) { +--- contrib/wpa/hs20/client/osu_client.h.orig ++++ contrib/wpa/hs20/client/osu_client.h +@@ -47,6 +47,7 @@ + int client_cert_present; + char **server_dnsname; + size_t server_dnsname_count; ++ const char *osu_ssid; /* Enforced OSU_SSID for testing purposes */ + #define WORKAROUND_OCSP_OPTIONAL 0x00000001 + unsigned long int workarounds; + }; +--- contrib/wpa/patches/openssl-0.9.8za-tls-extensions.patch.orig ++++ contrib/wpa/patches/openssl-0.9.8za-tls-extensions.patch +@@ -1,397 +0,0 @@ +-This patch adds support for TLS SessionTicket extension (RFC 5077) for +-the parts used by EAP-FAST (RFC 4851). +- +-This is based on the patch from Alexey Kobozev +-(sent to openssl-dev mailing list on Tue, 07 Jun 2005 15:40:58 +0300). +- +-OpenSSL 0.9.8za does not enable TLS extension support by default, so it +-will need to be enabled by adding enable-tlsext to config script +-command line. +- +- +-diff -upr openssl-0.9.8za.orig/ssl/s3_clnt.c openssl-0.9.8za/ssl/s3_clnt.c +---- openssl-0.9.8za.orig/ssl/s3_clnt.c 2014-06-05 11:09:26.000000000 +0300 +-+++ openssl-0.9.8za/ssl/s3_clnt.c 2014-06-05 20:37:09.221387312 +0300 +-@@ -767,6 +767,22 @@ int ssl3_get_server_hello(SSL *s) +- goto f_err; +- } +- +-+#ifndef OPENSSL_NO_TLSEXT +-+ /* check if we want to resume the session based on external pre-shared secret */ +-+ if (s->version >= TLS1_VERSION && s->tls_session_secret_cb) +-+ { +-+ SSL_CIPHER *pref_cipher=NULL; +-+ s->session->master_key_length=sizeof(s->session->master_key); +-+ if (s->tls_session_secret_cb(s, s->session->master_key, &s->session->master_key_length, +-+ NULL, &pref_cipher, s->tls_session_secret_cb_arg)) +-+ { +-+ s->session->cipher=pref_cipher ? +-+ pref_cipher : ssl_get_cipher_by_char(s,p+j); +-+ s->s3->flags |= SSL3_FLAGS_CCS_OK; +-+ } +-+ } +-+#endif /* OPENSSL_NO_TLSEXT */ +-+ +- if (j != 0 && j == s->session->session_id_length +- && memcmp(p,s->session->session_id,j) == 0) +- { +-@@ -2745,11 +2760,8 @@ int ssl3_check_finished(SSL *s) +- { +- int ok; +- long n; +-- /* If we have no ticket or session ID is non-zero length (a match of +-- * a non-zero session length would never reach here) it cannot be a +-- * resumed session. +-- */ +-- if (!s->session->tlsext_tick || s->session->session_id_length) +-+ /* If we have no ticket it cannot be a resumed session. */ +-+ if (!s->session->tlsext_tick) +- return 1; +- /* this function is called when we really expect a Certificate +- * message, so permit appropriate message length */ +-diff -upr openssl-0.9.8za.orig/ssl/s3_srvr.c openssl-0.9.8za/ssl/s3_srvr.c +---- openssl-0.9.8za.orig/ssl/s3_srvr.c 2014-06-05 11:09:26.000000000 +0300 +-+++ openssl-0.9.8za/ssl/s3_srvr.c 2014-06-05 20:37:09.225387312 +0300 +-@@ -1011,6 +1011,59 @@ int ssl3_get_client_hello(SSL *s) +- SSLerr(SSL_F_SSL3_GET_CLIENT_HELLO,SSL_R_CLIENTHELLO_TLSEXT); +- goto err; +- } +-+ +-+ /* Check if we want to use external pre-shared secret for this +-+ * handshake for not reused session only. We need to generate +-+ * server_random before calling tls_session_secret_cb in order to allow +-+ * SessionTicket processing to use it in key derivation. */ +-+ { +-+ unsigned long Time; +-+ unsigned char *pos; +-+ Time=(unsigned long)time(NULL); /* Time */ +-+ pos=s->s3->server_random; +-+ l2n(Time,pos); +-+ if (RAND_pseudo_bytes(pos,SSL3_RANDOM_SIZE-4) <= 0) +-+ { +-+ al=SSL_AD_INTERNAL_ERROR; +-+ goto f_err; +-+ } +-+ } +-+ +-+ if (!s->hit && s->version >= TLS1_VERSION && s->tls_session_secret_cb) +-+ { +-+ SSL_CIPHER *pref_cipher=NULL; +-+ +-+ s->session->master_key_length=sizeof(s->session->master_key); +-+ if(s->tls_session_secret_cb(s, s->session->master_key, &s->session->master_key_length, +-+ ciphers, &pref_cipher, s->tls_session_secret_cb_arg)) +-+ { +-+ s->hit=1; +-+ s->session->ciphers=ciphers; +-+ s->session->verify_result=X509_V_OK; +-+ +-+ ciphers=NULL; +-+ +-+ /* check if some cipher was preferred by call back */ +-+ pref_cipher=pref_cipher ? pref_cipher : ssl3_choose_cipher(s, s->session->ciphers, SSL_get_ciphers(s)); +-+ if (pref_cipher == NULL) +-+ { +-+ al=SSL_AD_HANDSHAKE_FAILURE; +-+ SSLerr(SSL_F_SSL3_GET_CLIENT_HELLO,SSL_R_NO_SHARED_CIPHER); +-+ goto f_err; +-+ } +-+ +-+ s->session->cipher=pref_cipher; +-+ +-+ if (s->cipher_list) +-+ sk_SSL_CIPHER_free(s->cipher_list); +-+ +-+ if (s->cipher_list_by_id) +-+ sk_SSL_CIPHER_free(s->cipher_list_by_id); +-+ +-+ s->cipher_list = sk_SSL_CIPHER_dup(s->session->ciphers); +-+ s->cipher_list_by_id = sk_SSL_CIPHER_dup(s->session->ciphers); +-+ } +-+ } +- #endif +- /* Worst case, we will use the NULL compression, but if we have other +- * options, we will now look for them. We have i-1 compression +-@@ -1161,16 +1214,22 @@ int ssl3_send_server_hello(SSL *s) +- unsigned char *buf; +- unsigned char *p,*d; +- int i,sl; +-- unsigned long l,Time; +-+ unsigned long l; +-+#ifdef OPENSSL_NO_TLSEXT +-+ unsigned long Time; +-+#endif +- +- if (s->state == SSL3_ST_SW_SRVR_HELLO_A) +- { +- buf=(unsigned char *)s->init_buf->data; +-+#ifdef OPENSSL_NO_TLSEXT +- p=s->s3->server_random; +-+ /* Generate server_random if it was not needed previously */ +- Time=(unsigned long)time(NULL); /* Time */ +- l2n(Time,p); +- if (RAND_pseudo_bytes(p,SSL3_RANDOM_SIZE-4) <= 0) +- return -1; +-+#endif +- /* Do the message type and length last */ +- d=p= &(buf[4]); +- +-diff -upr openssl-0.9.8za.orig/ssl/ssl_err.c openssl-0.9.8za/ssl/ssl_err.c +---- openssl-0.9.8za.orig/ssl/ssl_err.c 2014-06-05 11:09:08.000000000 +0300 +-+++ openssl-0.9.8za/ssl/ssl_err.c 2014-06-05 20:37:09.225387312 +0300 +-@@ -265,6 +265,7 @@ static ERR_STRING_DATA SSL_str_functs[]= +- {ERR_FUNC(SSL_F_TLS1_ENC), "TLS1_ENC"}, +- {ERR_FUNC(SSL_F_TLS1_SETUP_KEY_BLOCK), "TLS1_SETUP_KEY_BLOCK"}, +- {ERR_FUNC(SSL_F_WRITE_PENDING), "WRITE_PENDING"}, +-+{ERR_FUNC(SSL_F_SSL_SET_SESSION_TICKET_EXT), "SSL_set_session_ticket_ext"}, +- {0,NULL} +- }; +- +-diff -upr openssl-0.9.8za.orig/ssl/ssl.h openssl-0.9.8za/ssl/ssl.h +---- openssl-0.9.8za.orig/ssl/ssl.h 2014-06-05 11:09:08.000000000 +0300 +-+++ openssl-0.9.8za/ssl/ssl.h 2014-06-05 20:37:09.229387312 +0300 +-@@ -344,6 +344,7 @@ extern "C" { +- * 'struct ssl_st *' function parameters used to prototype callbacks +- * in SSL_CTX. */ +- typedef struct ssl_st *ssl_crock_st; +-+typedef struct tls_session_ticket_ext_st TLS_SESSION_TICKET_EXT; +- +- /* used to hold info on the particular ciphers used */ +- typedef struct ssl_cipher_st +-@@ -362,6 +363,9 @@ typedef struct ssl_cipher_st +- +- DECLARE_STACK_OF(SSL_CIPHER) +- +-+typedef int (*tls_session_ticket_ext_cb_fn)(SSL *s, const unsigned char *data, int len, void *arg); +-+typedef int (*tls_session_secret_cb_fn)(SSL *s, void *secret, int *secret_len, STACK_OF(SSL_CIPHER) *peer_ciphers, SSL_CIPHER **cipher, void *arg); +-+ +- /* Used to hold functions for SSLv2 or SSLv3/TLSv1 functions */ +- typedef struct ssl_method_st +- { +-@@ -1053,6 +1057,18 @@ struct ssl_st +- +- /* RFC4507 session ticket expected to be received or sent */ +- int tlsext_ticket_expected; +-+ +-+ /* TLS Session Ticket extension override */ +-+ TLS_SESSION_TICKET_EXT *tlsext_session_ticket; +-+ +-+ /* TLS Session Ticket extension callback */ +-+ tls_session_ticket_ext_cb_fn tls_session_ticket_ext_cb; +-+ void *tls_session_ticket_ext_cb_arg; +-+ +-+ /* TLS pre-shared secret session resumption */ +-+ tls_session_secret_cb_fn tls_session_secret_cb; +-+ void *tls_session_secret_cb_arg; +-+ +- SSL_CTX * initial_ctx; /* initial ctx, used to store sessions */ +- #define session_ctx initial_ctx +- #else +-@@ -1668,6 +1684,15 @@ void *SSL_COMP_get_compression_methods(v +- int SSL_COMP_add_compression_method(int id,void *cm); +- #endif +- +-+/* TLS extensions functions */ +-+int SSL_set_session_ticket_ext(SSL *s, void *ext_data, int ext_len); +-+ +-+int SSL_set_session_ticket_ext_cb(SSL *s, tls_session_ticket_ext_cb_fn cb, +-+ void *arg); +-+ +-+/* Pre-shared secret session resumption functions */ +-+int SSL_set_session_secret_cb(SSL *s, tls_session_secret_cb_fn tls_session_secret_cb, void *arg); +-+ +- /* BEGIN ERROR CODES */ +- /* The following lines are auto generated by the script mkerr.pl. Any changes +- * made after this point may be overwritten when the script is next run. +-@@ -1872,6 +1897,7 @@ void ERR_load_SSL_strings(void); +- #define SSL_F_TLS1_ENC 210 +- #define SSL_F_TLS1_SETUP_KEY_BLOCK 211 +- #define SSL_F_WRITE_PENDING 212 +-+#define SSL_F_SSL_SET_SESSION_TICKET_EXT 213 +- +- /* Reason codes. */ +- #define SSL_R_APP_DATA_IN_HANDSHAKE 100 +-diff -upr openssl-0.9.8za.orig/ssl/ssl_sess.c openssl-0.9.8za/ssl/ssl_sess.c +---- openssl-0.9.8za.orig/ssl/ssl_sess.c 2014-06-05 11:09:08.000000000 +0300 +-+++ openssl-0.9.8za/ssl/ssl_sess.c 2014-06-05 20:37:09.229387312 +0300 +-@@ -712,6 +712,61 @@ long SSL_CTX_get_timeout(const SSL_CTX * +- return(s->session_timeout); +- } +- +-+#ifndef OPENSSL_NO_TLSEXT +-+int SSL_set_session_secret_cb(SSL *s, int (*tls_session_secret_cb)(SSL *s, void *secret, int *secret_len, +-+ STACK_OF(SSL_CIPHER) *peer_ciphers, SSL_CIPHER **cipher, void *arg), void *arg) +-+ { +-+ if (s == NULL) return(0); +-+ s->tls_session_secret_cb = tls_session_secret_cb; +-+ s->tls_session_secret_cb_arg = arg; +-+ return(1); +-+ } +-+ +-+int SSL_set_session_ticket_ext_cb(SSL *s, tls_session_ticket_ext_cb_fn cb, +-+ void *arg) +-+ { +-+ if (s == NULL) return(0); +-+ s->tls_session_ticket_ext_cb = cb; +-+ s->tls_session_ticket_ext_cb_arg = arg; +-+ return(1); +-+ } +-+ +-+int SSL_set_session_ticket_ext(SSL *s, void *ext_data, int ext_len) +-+ { +-+ if (s->version >= TLS1_VERSION) +-+ { +-+ if (s->tlsext_session_ticket) +-+ { +-+ OPENSSL_free(s->tlsext_session_ticket); +-+ s->tlsext_session_ticket = NULL; +-+ } +-+ +-+ s->tlsext_session_ticket = OPENSSL_malloc(sizeof(TLS_SESSION_TICKET_EXT) + ext_len); +-+ if (!s->tlsext_session_ticket) +-+ { +-+ SSLerr(SSL_F_SSL_SET_SESSION_TICKET_EXT, ERR_R_MALLOC_FAILURE); +-+ return 0; +-+ } +-+ +-+ if (ext_data) +-+ { +-+ s->tlsext_session_ticket->length = ext_len; +-+ s->tlsext_session_ticket->data = s->tlsext_session_ticket + 1; +-+ memcpy(s->tlsext_session_ticket->data, ext_data, ext_len); +-+ } +-+ else +-+ { +-+ s->tlsext_session_ticket->length = 0; +-+ s->tlsext_session_ticket->data = NULL; +-+ } +-+ +-+ return 1; +-+ } +-+ +-+ return 0; +-+ } +-+#endif /* OPENSSL_NO_TLSEXT */ +-+ +- typedef struct timeout_param_st +- { +- SSL_CTX *ctx; +-diff -upr openssl-0.9.8za.orig/ssl/t1_lib.c openssl-0.9.8za/ssl/t1_lib.c +---- openssl-0.9.8za.orig/ssl/t1_lib.c 2014-06-05 11:09:08.000000000 +0300 +-+++ openssl-0.9.8za/ssl/t1_lib.c 2014-06-05 20:37:09.229387312 +0300 +-@@ -106,6 +106,12 @@ int tls1_new(SSL *s) +- +- void tls1_free(SSL *s) +- { +-+#ifndef OPENSSL_NO_TLSEXT +-+ if (s->tlsext_session_ticket) +-+ { +-+ OPENSSL_free(s->tlsext_session_ticket); +-+ } +-+#endif +- ssl3_free(s); +- } +- +-@@ -206,8 +212,23 @@ unsigned char *ssl_add_clienthello_tlsex +- int ticklen; +- if (!s->new_session && s->session && s->session->tlsext_tick) +- ticklen = s->session->tlsext_ticklen; +-+ else if (s->session && s->tlsext_session_ticket && +-+ s->tlsext_session_ticket->data) +-+ { +-+ ticklen = s->tlsext_session_ticket->length; +-+ s->session->tlsext_tick = OPENSSL_malloc(ticklen); +-+ if (!s->session->tlsext_tick) +-+ return NULL; +-+ memcpy(s->session->tlsext_tick, +-+ s->tlsext_session_ticket->data, +-+ ticklen); +-+ s->session->tlsext_ticklen = ticklen; +-+ } +- else +- ticklen = 0; +-+ if (ticklen == 0 && s->tlsext_session_ticket && +-+ s->tlsext_session_ticket->data == NULL) +-+ goto skip_ext; +- /* Check for enough room 2 for extension type, 2 for len +- * rest for ticket +- */ +-@@ -221,6 +242,7 @@ unsigned char *ssl_add_clienthello_tlsex +- ret += ticklen; +- } +- } +-+ skip_ext: +- +- if (s->tlsext_status_type == TLSEXT_STATUSTYPE_ocsp && +- s->version != DTLS1_VERSION) +-@@ -574,6 +596,15 @@ int ssl_parse_clienthello_tlsext(SSL *s, +- return 0; +- renegotiate_seen = 1; +- } +-+ else if (type == TLSEXT_TYPE_session_ticket) +-+ { +-+ if (s->tls_session_ticket_ext_cb && +-+ !s->tls_session_ticket_ext_cb(s, data, size, s->tls_session_ticket_ext_cb_arg)) +-+ { +-+ *al = TLS1_AD_INTERNAL_ERROR; +-+ return 0; +-+ } +-+ } +- else if (type == TLSEXT_TYPE_status_request && +- s->version != DTLS1_VERSION && s->ctx->tlsext_status_cb) +- { +-@@ -751,6 +782,12 @@ int ssl_parse_serverhello_tlsext(SSL *s, +- } +- else if (type == TLSEXT_TYPE_session_ticket) +- { +-+ if (s->tls_session_ticket_ext_cb && +-+ !s->tls_session_ticket_ext_cb(s, data, size, s->tls_session_ticket_ext_cb_arg)) +-+ { +-+ *al = TLS1_AD_INTERNAL_ERROR; +-+ return 0; +-+ } +- if ((SSL_get_options(s) & SSL_OP_NO_TICKET) +- || (size > 0)) +- { +-@@ -1043,6 +1080,15 @@ int tls1_process_ticket(SSL *s, unsigned +- s->tlsext_ticket_expected = 1; +- return 0; /* Cache miss */ +- } +-+ if (s->tls_session_secret_cb) +-+ { +-+ /* Indicate cache miss here and instead of +-+ * generating the session from ticket now, +-+ * trigger abbreviated handshake based on +-+ * external mechanism to calculate the master +-+ * secret later. */ +-+ return 0; +-+ } +- return tls_decrypt_ticket(s, p, size, session_id, len, +- ret); +- } +-diff -upr openssl-0.9.8za.orig/ssl/tls1.h openssl-0.9.8za/ssl/tls1.h +---- openssl-0.9.8za.orig/ssl/tls1.h 2014-06-05 11:09:08.000000000 +0300 +-+++ openssl-0.9.8za/ssl/tls1.h 2014-06-05 20:37:09.229387312 +0300 +-@@ -415,6 +415,13 @@ SSL_CTX_callback_ctrl(ssl,SSL_CTRL_SET_T +- #define TLS_MD_MASTER_SECRET_CONST "\x6d\x61\x73\x74\x65\x72\x20\x73\x65\x63\x72\x65\x74" /*master secret*/ +- #endif +- +-+/* TLS extension struct */ +-+struct tls_session_ticket_ext_st +-+ { +-+ unsigned short length; +-+ void *data; +-+ }; +-+ +- #ifdef __cplusplus +- } +- #endif +-diff -upr openssl-0.9.8za.orig/util/ssleay.num openssl-0.9.8za/util/ssleay.num +---- openssl-0.9.8za.orig/util/ssleay.num 2014-06-05 12:38:45.000000000 +0300 +-+++ openssl-0.9.8za/util/ssleay.num 2014-06-05 20:37:09.229387312 +0300 +-@@ -242,3 +242,5 @@ SSL_set_SSL_CTX +- SSL_get_servername 291 EXIST::FUNCTION:TLSEXT +- SSL_get_servername_type 292 EXIST::FUNCTION:TLSEXT +- SSL_CTX_set_client_cert_engine 293 EXIST::FUNCTION:ENGINE +-+SSL_set_session_ticket_ext 306 EXIST::FUNCTION:TLSEXT +-+SSL_set_session_secret_cb 307 EXIST::FUNCTION:TLSEXT +--- contrib/wpa/patches/openssl-0.9.8zf-tls-extensions.patch.orig ++++ contrib/wpa/patches/openssl-0.9.8zf-tls-extensions.patch +@@ -1,398 +0,0 @@ +-This patch adds support for TLS SessionTicket extension (RFC 5077) for +-the parts used by EAP-FAST (RFC 4851). +- +-This is based on the patch from Alexey Kobozev +-(sent to openssl-dev mailing list on Tue, 07 Jun 2005 15:40:58 +0300). +- +-OpenSSL 0.9.8zf does not enable TLS extension support by default, so it +-will need to be enabled by adding enable-tlsext to config script +-command line. +- +- +-diff -upr openssl-0.9.8zf.orig/ssl/s3_clnt.c openssl-0.9.8zf/ssl/s3_clnt.c +---- openssl-0.9.8zf.orig/ssl/s3_clnt.c 2015-03-19 15:46:46.000000000 +0200 +-+++ openssl-0.9.8zf/ssl/s3_clnt.c 2015-03-24 16:19:14.043911769 +0200 +-@@ -760,6 +760,23 @@ int ssl3_get_server_hello(SSL *s) +- goto f_err; +- } +- +-+#ifndef OPENSSL_NO_TLSEXT +-+ /* check if we want to resume the session based on external pre-shared secret */ +-+ if (s->version >= TLS1_VERSION && s->tls_session_secret_cb) { +-+ SSL_CIPHER *pref_cipher = NULL; +-+ +-+ s->session->master_key_length = sizeof(s->session->master_key); +-+ if (s->tls_session_secret_cb(s, s->session->master_key, +-+ &s->session->master_key_length, +-+ NULL, &pref_cipher, +-+ s->tls_session_secret_cb_arg)) { +-+ s->session->cipher = pref_cipher ? +-+ pref_cipher : ssl_get_cipher_by_char(s, p + j); +-+ s->s3->flags |= SSL3_FLAGS_CCS_OK; +-+ } +-+ } +-+#endif /* OPENSSL_NO_TLSEXT */ +-+ +- if (j != 0 && j == s->session->session_id_length +- && memcmp(p, s->session->session_id, j) == 0) { +- if (s->sid_ctx_length != s->session->sid_ctx_length +-@@ -2684,12 +2701,8 @@ int ssl3_check_finished(SSL *s) +- { +- int ok; +- long n; +-- /* +-- * If we have no ticket or session ID is non-zero length (a match of a +-- * non-zero session length would never reach here) it cannot be a resumed +-- * session. +-- */ +-- if (!s->session->tlsext_tick || s->session->session_id_length) +-+ /* If we have no ticket it cannot be a resumed session. */ +-+ if (!s->session->tlsext_tick) +- return 1; +- /* +- * this function is called when we really expect a Certificate message, +-diff -upr openssl-0.9.8zf.orig/ssl/s3_srvr.c openssl-0.9.8zf/ssl/s3_srvr.c +---- openssl-0.9.8zf.orig/ssl/s3_srvr.c 2015-03-19 15:46:46.000000000 +0200 +-+++ openssl-0.9.8zf/ssl/s3_srvr.c 2015-03-24 16:23:34.567909681 +0200 +-@@ -999,6 +999,59 @@ int ssl3_get_client_hello(SSL *s) +- SSLerr(SSL_F_SSL3_GET_CLIENT_HELLO, SSL_R_CLIENTHELLO_TLSEXT); +- goto err; +- } +-+ +-+ /* Check if we want to use external pre-shared secret for this +-+ * handshake for not reused session only. We need to generate +-+ * server_random before calling tls_session_secret_cb in order to allow +-+ * SessionTicket processing to use it in key derivation. */ +-+ { +-+ unsigned long Time; +-+ unsigned char *pos; +-+ Time = (unsigned long)time(NULL); /* Time */ +-+ pos = s->s3->server_random; +-+ l2n(Time, pos); +-+ if (RAND_pseudo_bytes(pos, SSL3_RANDOM_SIZE - 4) <= 0) { +-+ al = SSL_AD_INTERNAL_ERROR; +-+ goto f_err; +-+ } +-+ } +-+ +-+ if (!s->hit && s->version >= TLS1_VERSION && s->tls_session_secret_cb) { +-+ SSL_CIPHER *pref_cipher = NULL; +-+ +-+ s->session->master_key_length = sizeof(s->session->master_key); +-+ if (s->tls_session_secret_cb(s, s->session->master_key, +-+ &s->session->master_key_length, +-+ ciphers, &pref_cipher, +-+ s->tls_session_secret_cb_arg)) { +-+ s->hit = 1; +-+ s->session->ciphers = ciphers; +-+ s->session->verify_result = X509_V_OK; +-+ +-+ ciphers = NULL; +-+ +-+ /* check if some cipher was preferred by call back */ +-+ pref_cipher = pref_cipher ? pref_cipher : +-+ ssl3_choose_cipher(s, s->session->ciphers, +-+ SSL_get_ciphers(s)); +-+ if (pref_cipher == NULL) { +-+ al = SSL_AD_HANDSHAKE_FAILURE; +-+ SSLerr(SSL_F_SSL3_GET_CLIENT_HELLO, SSL_R_NO_SHARED_CIPHER); +-+ goto f_err; +-+ } +-+ +-+ s->session->cipher = pref_cipher; +-+ +-+ if (s->cipher_list) +-+ sk_SSL_CIPHER_free(s->cipher_list); +-+ +-+ if (s->cipher_list_by_id) +-+ sk_SSL_CIPHER_free(s->cipher_list_by_id); +-+ +-+ s->cipher_list = sk_SSL_CIPHER_dup(s->session->ciphers); +-+ s->cipher_list_by_id = sk_SSL_CIPHER_dup(s->session->ciphers); +-+ } +-+ } +- #endif +- /* +- * Worst case, we will use the NULL compression, but if we have other +-@@ -1143,15 +1196,21 @@ int ssl3_send_server_hello(SSL *s) +- unsigned char *buf; +- unsigned char *p, *d; +- int i, sl; +-- unsigned long l, Time; +-+ unsigned long l; +-+#ifdef OPENSSL_NO_TLSEXT +-+ unsigned long Time; +-+#endif +- +- if (s->state == SSL3_ST_SW_SRVR_HELLO_A) { +- buf = (unsigned char *)s->init_buf->data; +-+#ifdef OPENSSL_NO_TLSEXT +- p = s->s3->server_random; +-+ /* Generate server_random if it was not needed previously */ +- Time = (unsigned long)time(NULL); /* Time */ +- l2n(Time, p); +- if (RAND_pseudo_bytes(p, SSL3_RANDOM_SIZE - 4) <= 0) +- return -1; +-+#endif +- /* Do the message type and length last */ +- d = p = &(buf[4]); +- +-diff -upr openssl-0.9.8zf.orig/ssl/ssl_err.c openssl-0.9.8zf/ssl/ssl_err.c +---- openssl-0.9.8zf.orig/ssl/ssl_err.c 2015-03-19 15:46:46.000000000 +0200 +-+++ openssl-0.9.8zf/ssl/ssl_err.c 2015-03-24 16:35:58.627903717 +0200 +-@@ -316,6 +316,7 @@ static ERR_STRING_DATA SSL_str_functs[] +- {ERR_FUNC(SSL_F_TLS1_ENC), "TLS1_ENC"}, +- {ERR_FUNC(SSL_F_TLS1_SETUP_KEY_BLOCK), "TLS1_SETUP_KEY_BLOCK"}, +- {ERR_FUNC(SSL_F_WRITE_PENDING), "WRITE_PENDING"}, +-+ {ERR_FUNC(SSL_F_SSL_SET_SESSION_TICKET_EXT), "SSL_set_session_ticket_ext"}, +- {0, NULL} +- }; +- +-diff -upr openssl-0.9.8zf.orig/ssl/ssl.h openssl-0.9.8zf/ssl/ssl.h +---- openssl-0.9.8zf.orig/ssl/ssl.h 2015-03-19 15:46:46.000000000 +0200 +-+++ openssl-0.9.8zf/ssl/ssl.h 2015-03-24 16:25:44.339908641 +0200 +-@@ -349,6 +349,7 @@ extern "C" { +- * function parameters used to prototype callbacks in SSL_CTX. +- */ +- typedef struct ssl_st *ssl_crock_st; +-+typedef struct tls_session_ticket_ext_st TLS_SESSION_TICKET_EXT; +- +- /* used to hold info on the particular ciphers used */ +- typedef struct ssl_cipher_st { +-@@ -366,6 +367,12 @@ typedef struct ssl_cipher_st { +- +- DECLARE_STACK_OF(SSL_CIPHER) +- +-+typedef int (*tls_session_ticket_ext_cb_fn)(SSL *s, const unsigned char *data, +-+ int len, void *arg); +-+typedef int (*tls_session_secret_cb_fn)(SSL *s, void *secret, int *secret_len, +-+ STACK_OF(SSL_CIPHER) *peer_ciphers, +-+ SSL_CIPHER **cipher, void *arg); +-+ +- /* Used to hold functions for SSLv2 or SSLv3/TLSv1 functions */ +- typedef struct ssl_method_st { +- int version; +-@@ -1116,6 +1123,18 @@ struct ssl_st { +- int tlsext_ocsp_resplen; +- /* RFC4507 session ticket expected to be received or sent */ +- int tlsext_ticket_expected; +-+ +-+ /* TLS Session Ticket extension override */ +-+ TLS_SESSION_TICKET_EXT *tlsext_session_ticket; +-+ +-+ /* TLS Session Ticket extension callback */ +-+ tls_session_ticket_ext_cb_fn tls_session_ticket_ext_cb; +-+ void *tls_session_ticket_ext_cb_arg; +-+ +-+ /* TLS pre-shared secret session resumption */ +-+ tls_session_secret_cb_fn tls_session_secret_cb; +-+ void *tls_session_secret_cb_arg; +-+ +- SSL_CTX *initial_ctx; /* initial ctx, used to store sessions */ +- # define session_ctx initial_ctx +- # else +-@@ -1772,6 +1791,17 @@ void *SSL_COMP_get_compression_methods(v +- int SSL_COMP_add_compression_method(int id, void *cm); +- # endif +- +-+/* TLS extensions functions */ +-+int SSL_set_session_ticket_ext(SSL *s, void *ext_data, int ext_len); +-+ +-+int SSL_set_session_ticket_ext_cb(SSL *s, tls_session_ticket_ext_cb_fn cb, +-+ void *arg); +-+ +-+/* Pre-shared secret session resumption functions */ +-+int SSL_set_session_secret_cb(SSL *s, +-+ tls_session_secret_cb_fn tls_session_secret_cb, +-+ void *arg); +-+ +- /* BEGIN ERROR CODES */ +- /* +- * The following lines are auto generated by the script mkerr.pl. Any changes +-@@ -1977,6 +2007,7 @@ void ERR_load_SSL_strings(void); +- # define SSL_F_TLS1_ENC 210 +- # define SSL_F_TLS1_SETUP_KEY_BLOCK 211 +- # define SSL_F_WRITE_PENDING 212 +-+#define SSL_F_SSL_SET_SESSION_TICKET_EXT 213 +- +- /* Reason codes. */ +- # define SSL_R_APP_DATA_IN_HANDSHAKE 100 +-diff -upr openssl-0.9.8zf.orig/ssl/ssl_sess.c openssl-0.9.8zf/ssl/ssl_sess.c +---- openssl-0.9.8zf.orig/ssl/ssl_sess.c 2015-03-19 15:46:46.000000000 +0200 +-+++ openssl-0.9.8zf/ssl/ssl_sess.c 2015-03-24 16:28:04.819907515 +0200 +-@@ -716,6 +716,61 @@ long SSL_CTX_get_timeout(const SSL_CTX * +- return (s->session_timeout); +- } +- +-+#ifndef OPENSSL_NO_TLSEXT +-+int SSL_set_session_secret_cb( +-+ SSL *s, +-+ int (*tls_session_secret_cb)(SSL *s, void *secret, int *secret_len, +-+ STACK_OF(SSL_CIPHER) *peer_ciphers, +-+ SSL_CIPHER **cipher, void *arg), void *arg) +-+{ +-+ if (s == NULL) +-+ return 0; +-+ s->tls_session_secret_cb = tls_session_secret_cb; +-+ s->tls_session_secret_cb_arg = arg; +-+ return 1; +-+} +-+ +-+int SSL_set_session_ticket_ext_cb(SSL *s, tls_session_ticket_ext_cb_fn cb, +-+ void *arg) +-+{ +-+ if (s == NULL) +-+ return 0; +-+ s->tls_session_ticket_ext_cb = cb; +-+ s->tls_session_ticket_ext_cb_arg = arg; +-+ return 1; +-+} +-+ +-+int SSL_set_session_ticket_ext(SSL *s, void *ext_data, int ext_len) +-+{ +-+ if (s->version >= TLS1_VERSION) { +-+ if (s->tlsext_session_ticket) { +-+ OPENSSL_free(s->tlsext_session_ticket); +-+ s->tlsext_session_ticket = NULL; +-+ } +-+ +-+ s->tlsext_session_ticket = OPENSSL_malloc( +-+ sizeof(TLS_SESSION_TICKET_EXT) + ext_len); +-+ if (!s->tlsext_session_ticket) { +-+ SSLerr(SSL_F_SSL_SET_SESSION_TICKET_EXT, ERR_R_MALLOC_FAILURE); +-+ return 0; +-+ } +-+ +-+ if (ext_data) { +-+ s->tlsext_session_ticket->length = ext_len; +-+ s->tlsext_session_ticket->data = s->tlsext_session_ticket + 1; +-+ memcpy(s->tlsext_session_ticket->data, ext_data, ext_len); +-+ } else { +-+ s->tlsext_session_ticket->length = 0; +-+ s->tlsext_session_ticket->data = NULL; +-+ } +-+ +-+ return 1; +-+ } +-+ +-+ return 0; +-+} +-+#endif /* OPENSSL_NO_TLSEXT */ +-+ +- typedef struct timeout_param_st { +- SSL_CTX *ctx; +- long time; +-diff -upr openssl-0.9.8zf.orig/ssl/t1_lib.c openssl-0.9.8zf/ssl/t1_lib.c +---- openssl-0.9.8zf.orig/ssl/t1_lib.c 2015-03-19 15:46:46.000000000 +0200 +-+++ openssl-0.9.8zf/ssl/t1_lib.c 2015-03-24 16:32:46.923905254 +0200 +-@@ -108,6 +108,11 @@ int tls1_new(SSL *s) +- +- void tls1_free(SSL *s) +- { +-+#ifndef OPENSSL_NO_TLSEXT +-+ if (s->tlsext_session_ticket) { +-+ OPENSSL_free(s->tlsext_session_ticket); +-+ } +-+#endif +- ssl3_free(s); +- } +- +-@@ -206,8 +211,20 @@ unsigned char *ssl_add_clienthello_tlsex +- int ticklen; +- if (!s->new_session && s->session && s->session->tlsext_tick) +- ticklen = s->session->tlsext_ticklen; +-- else +-+ else if (s->session && s->tlsext_session_ticket && +-+ s->tlsext_session_ticket->data) { +-+ ticklen = s->tlsext_session_ticket->length; +-+ s->session->tlsext_tick = OPENSSL_malloc(ticklen); +-+ if (!s->session->tlsext_tick) +-+ return NULL; +-+ memcpy(s->session->tlsext_tick, s->tlsext_session_ticket->data, +-+ ticklen); +-+ s->session->tlsext_ticklen = ticklen; +-+ } else +- ticklen = 0; +-+ if (ticklen == 0 && s->tlsext_session_ticket && +-+ s->tlsext_session_ticket->data == NULL) +-+ goto skip_ext; +- /* +- * Check for enough room 2 for extension type, 2 for len rest for +- * ticket +-@@ -221,6 +238,7 @@ unsigned char *ssl_add_clienthello_tlsex +- ret += ticklen; +- } +- } +-+skip_ext: +- +- if (s->tlsext_status_type == TLSEXT_STATUSTYPE_ocsp && +- s->version != DTLS1_VERSION) { +-@@ -560,6 +578,14 @@ int ssl_parse_clienthello_tlsext(SSL *s, +- if (!ssl_parse_clienthello_renegotiate_ext(s, data, size, al)) +- return 0; +- renegotiate_seen = 1; +-+ } else if (type == TLSEXT_TYPE_session_ticket) { +-+ if (s->tls_session_ticket_ext_cb && +-+ !s->tls_session_ticket_ext_cb(s, data, size, +-+ s->tls_session_ticket_ext_cb_arg)) +-+ { +-+ *al = TLS1_AD_INTERNAL_ERROR; +-+ return 0; +-+ } +- } else if (type == TLSEXT_TYPE_status_request && +- s->version != DTLS1_VERSION && s->ctx->tlsext_status_cb) { +- +-@@ -710,6 +736,13 @@ int ssl_parse_serverhello_tlsext(SSL *s, +- } +- tlsext_servername = 1; +- } else if (type == TLSEXT_TYPE_session_ticket) { +-+ if (s->tls_session_ticket_ext_cb && +-+ !s->tls_session_ticket_ext_cb( +-+ s, data, size, +-+ s->tls_session_ticket_ext_cb_arg)) { +-+ *al = TLS1_AD_INTERNAL_ERROR; +-+ return 0; +-+ } +- if ((SSL_get_options(s) & SSL_OP_NO_TICKET) +- || (size > 0)) { +- *al = TLS1_AD_UNSUPPORTED_EXTENSION; +-@@ -993,6 +1026,14 @@ int tls1_process_ticket(SSL *s, unsigned +- s->tlsext_ticket_expected = 1; +- return 0; /* Cache miss */ +- } +-+ if (s->tls_session_secret_cb) { +-+ /* Indicate cache miss here and instead of +-+ * generating the session from ticket now, +-+ * trigger abbreviated handshake based on +-+ * external mechanism to calculate the master +-+ * secret later. */ +-+ return 0; +-+ } +- return tls_decrypt_ticket(s, p, size, session_id, len, ret); +- } +- p += size; +-diff -upr openssl-0.9.8zf.orig/ssl/tls1.h openssl-0.9.8zf/ssl/tls1.h +---- openssl-0.9.8zf.orig/ssl/tls1.h 2015-03-19 15:46:46.000000000 +0200 +-+++ openssl-0.9.8zf/ssl/tls1.h 2015-03-24 16:33:31.855904894 +0200 +-@@ -460,6 +460,12 @@ SSL_CTX_callback_ctrl(ssl,SSL_CTRL_SET_T +- # define TLS_MD_MASTER_SECRET_CONST "\x6d\x61\x73\x74\x65\x72\x20\x73\x65\x63\x72\x65\x74" +- # endif +- +-+/* TLS extension struct */ +-+struct tls_session_ticket_ext_st { +-+ unsigned short length; +-+ void *data; +-+}; +-+ +- #ifdef __cplusplus +- } +- #endif +-diff -upr openssl-0.9.8zf.orig/util/ssleay.num openssl-0.9.8zf/util/ssleay.num +---- openssl-0.9.8zf.orig/util/ssleay.num 2015-03-19 15:47:15.000000000 +0200 +-+++ openssl-0.9.8zf/util/ssleay.num 2015-03-24 16:33:51.127904739 +0200 +-@@ -242,3 +242,5 @@ SSL_set_SSL_CTX +- SSL_get_servername 291 EXIST::FUNCTION:TLSEXT +- SSL_get_servername_type 292 EXIST::FUNCTION:TLSEXT +- SSL_CTX_set_client_cert_engine 293 EXIST::FUNCTION:ENGINE +-+SSL_set_session_ticket_ext 306 EXIST::FUNCTION:TLSEXT +-+SSL_set_session_secret_cb 307 EXIST::FUNCTION:TLSEXT +--- contrib/wpa/src/ap/accounting.c.orig ++++ contrib/wpa/src/ap/accounting.c +@@ -1,6 +1,6 @@ + /* + * hostapd / RADIUS Accounting +- * Copyright (c) 2002-2009, 2012, Jouni Malinen ++ * Copyright (c) 2002-2009, 2012-2015, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. +@@ -41,6 +41,7 @@ + size_t len; + int i; + struct wpabuf *b; ++ struct os_time now; + + msg = radius_msg_new(RADIUS_CODE_ACCOUNTING_REQUEST, + radius_client_get_id(hapd->radius)); +@@ -49,27 +50,6 @@ + return NULL; + } + +- if (sta) { +- radius_msg_make_authenticator(msg, (u8 *) sta, sizeof(*sta)); +- +- if ((hapd->conf->wpa & 2) && +- !hapd->conf->disable_pmksa_caching && +- sta->eapol_sm && sta->eapol_sm->acct_multi_session_id_hi) { +- os_snprintf(buf, sizeof(buf), "%08X+%08X", +- sta->eapol_sm->acct_multi_session_id_hi, +- sta->eapol_sm->acct_multi_session_id_lo); +- if (!radius_msg_add_attr( +- msg, RADIUS_ATTR_ACCT_MULTI_SESSION_ID, +- (u8 *) buf, os_strlen(buf))) { +- wpa_printf(MSG_INFO, +- "Could not add Acct-Multi-Session-Id"); +- goto fail; +- } +- } +- } else { +- radius_msg_make_authenticator(msg, (u8 *) hapd, sizeof(*hapd)); +- } +- + if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_STATUS_TYPE, + status_type)) { + wpa_printf(MSG_INFO, "Could not add Acct-Status-Type"); +@@ -76,17 +56,18 @@ + goto fail; + } + +- if (!hostapd_config_get_radius_attr(hapd->conf->radius_acct_req_attr, +- RADIUS_ATTR_ACCT_AUTHENTIC) && +- !radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_AUTHENTIC, +- hapd->conf->ieee802_1x ? +- RADIUS_ACCT_AUTHENTIC_RADIUS : +- RADIUS_ACCT_AUTHENTIC_LOCAL)) { +- wpa_printf(MSG_INFO, "Could not add Acct-Authentic"); +- goto fail; +- } ++ if (sta) { ++ if (!hostapd_config_get_radius_attr( ++ hapd->conf->radius_acct_req_attr, ++ RADIUS_ATTR_ACCT_AUTHENTIC) && ++ !radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_AUTHENTIC, ++ hapd->conf->ieee802_1x ? ++ RADIUS_ACCT_AUTHENTIC_RADIUS : ++ RADIUS_ACCT_AUTHENTIC_LOCAL)) { ++ wpa_printf(MSG_INFO, "Could not add Acct-Authentic"); ++ goto fail; ++ } + +- if (sta) { + /* Use 802.1X identity if available */ + val = ieee802_1x_get_identity(sta->eapol_sm, &len); + +@@ -147,8 +128,34 @@ + wpa_printf(MSG_ERROR, "Could not add CUI from ACL"); + goto fail; + } ++ ++ if (sta->ipaddr && ++ !radius_msg_add_attr_int32(msg, ++ RADIUS_ATTR_FRAMED_IP_ADDRESS, ++ be_to_host32(sta->ipaddr))) { ++ wpa_printf(MSG_ERROR, ++ "Could not add Framed-IP-Address"); ++ goto fail; ++ } + } + ++ os_get_time(&now); ++ if (now.sec > 1000000000 && ++ !radius_msg_add_attr_int32(msg, RADIUS_ATTR_EVENT_TIMESTAMP, ++ now.sec)) { ++ wpa_printf(MSG_INFO, "Could not add Event-Timestamp"); ++ goto fail; ++ } ++ ++ /* ++ * Add Acct-Delay-Time with zero value for the first transmission. This ++ * will be updated within radius_client.c when retransmitting the frame. ++ */ ++ if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_DELAY_TIME, 0)) { ++ wpa_printf(MSG_INFO, "Could not add Acct-Delay-Time"); ++ goto fail; ++ } ++ + return msg; + + fail: +@@ -164,19 +171,25 @@ + if (hostapd_drv_read_sta_data(hapd, data, sta->addr)) + return -1; + +- if (sta->last_rx_bytes > data->rx_bytes) +- sta->acct_input_gigawords++; +- if (sta->last_tx_bytes > data->tx_bytes) +- sta->acct_output_gigawords++; +- sta->last_rx_bytes = data->rx_bytes; +- sta->last_tx_bytes = data->tx_bytes; ++ if (!data->bytes_64bit) { ++ /* Extend 32-bit counters from the driver to 64-bit counters */ ++ if (sta->last_rx_bytes_lo > data->rx_bytes) ++ sta->last_rx_bytes_hi++; ++ sta->last_rx_bytes_lo = data->rx_bytes; + ++ if (sta->last_tx_bytes_lo > data->tx_bytes) ++ sta->last_tx_bytes_hi++; ++ sta->last_tx_bytes_lo = data->tx_bytes; ++ } ++ + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_RADIUS, +- HOSTAPD_LEVEL_DEBUG, "updated TX/RX stats: " +- "Acct-Input-Octets=%lu Acct-Input-Gigawords=%u " +- "Acct-Output-Octets=%lu Acct-Output-Gigawords=%u", +- sta->last_rx_bytes, sta->acct_input_gigawords, +- sta->last_tx_bytes, sta->acct_output_gigawords); ++ HOSTAPD_LEVEL_DEBUG, ++ "updated TX/RX stats: rx_bytes=%llu [%u:%u] tx_bytes=%llu [%u:%u] bytes_64bit=%d", ++ data->rx_bytes, sta->last_rx_bytes_hi, ++ sta->last_rx_bytes_lo, ++ data->tx_bytes, sta->last_tx_bytes_hi, ++ sta->last_tx_bytes_lo, ++ data->bytes_64bit); + + return 0; + } +@@ -217,12 +230,14 @@ + + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_RADIUS, + HOSTAPD_LEVEL_INFO, +- "starting accounting session %08X-%08X", +- sta->acct_session_id_hi, sta->acct_session_id_lo); ++ "starting accounting session %016llX", ++ (unsigned long long) sta->acct_session_id); + + os_get_reltime(&sta->acct_session_start); +- sta->last_rx_bytes = sta->last_tx_bytes = 0; +- sta->acct_input_gigawords = sta->acct_output_gigawords = 0; ++ sta->last_rx_bytes_hi = 0; ++ sta->last_rx_bytes_lo = 0; ++ sta->last_tx_bytes_hi = 0; ++ sta->last_tx_bytes_lo = 0; + hostapd_drv_sta_clear_stats(hapd, sta->addr); + + if (!hapd->conf->radius->acct_server) +@@ -251,8 +266,7 @@ + int cause = sta->acct_terminate_cause; + struct hostap_sta_driver_data data; + struct os_reltime now_r, diff; +- struct os_time now; +- u32 gigawords; ++ u64 bytes; + + if (!hapd->conf->radius->acct_server) + return; +@@ -266,7 +280,6 @@ + } + + os_get_reltime(&now_r); +- os_get_time(&now); + os_reltime_sub(&now_r, &sta->acct_session_start, &diff); + if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_SESSION_TIME, + diff.sec)) { +@@ -287,48 +300,42 @@ + wpa_printf(MSG_INFO, "Could not add Acct-Output-Packets"); + goto fail; + } ++ if (data.bytes_64bit) ++ bytes = data.rx_bytes; ++ else ++ bytes = ((u64) sta->last_rx_bytes_hi << 32) | ++ sta->last_rx_bytes_lo; + if (!radius_msg_add_attr_int32(msg, + RADIUS_ATTR_ACCT_INPUT_OCTETS, +- data.rx_bytes)) { ++ (u32) bytes)) { + wpa_printf(MSG_INFO, "Could not add Acct-Input-Octets"); + goto fail; + } +- gigawords = sta->acct_input_gigawords; +-#if __WORDSIZE == 64 +- gigawords += data.rx_bytes >> 32; +-#endif +- if (gigawords && +- !radius_msg_add_attr_int32( +- msg, RADIUS_ATTR_ACCT_INPUT_GIGAWORDS, +- gigawords)) { ++ if (!radius_msg_add_attr_int32(msg, ++ RADIUS_ATTR_ACCT_INPUT_GIGAWORDS, ++ (u32) (bytes >> 32))) { + wpa_printf(MSG_INFO, "Could not add Acct-Input-Gigawords"); + goto fail; + } ++ if (data.bytes_64bit) ++ bytes = data.tx_bytes; ++ else ++ bytes = ((u64) sta->last_tx_bytes_hi << 32) | ++ sta->last_tx_bytes_lo; + if (!radius_msg_add_attr_int32(msg, + RADIUS_ATTR_ACCT_OUTPUT_OCTETS, +- data.tx_bytes)) { ++ (u32) bytes)) { + wpa_printf(MSG_INFO, "Could not add Acct-Output-Octets"); + goto fail; + } +- gigawords = sta->acct_output_gigawords; +-#if __WORDSIZE == 64 +- gigawords += data.tx_bytes >> 32; +-#endif +- if (gigawords && +- !radius_msg_add_attr_int32( +- msg, RADIUS_ATTR_ACCT_OUTPUT_GIGAWORDS, +- gigawords)) { ++ if (!radius_msg_add_attr_int32(msg, ++ RADIUS_ATTR_ACCT_OUTPUT_GIGAWORDS, ++ (u32) (bytes >> 32))) { + wpa_printf(MSG_INFO, "Could not add Acct-Output-Gigawords"); + goto fail; + } + } + +- if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_EVENT_TIMESTAMP, +- now.sec)) { +- wpa_printf(MSG_INFO, "Could not add Event-Timestamp"); +- goto fail; +- } +- + if (eloop_terminated()) + cause = RADIUS_ACCT_TERMINATE_CAUSE_ADMIN_REBOOT; + +@@ -375,22 +382,17 @@ + eloop_cancel_timeout(accounting_interim_update, hapd, sta); + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_RADIUS, + HOSTAPD_LEVEL_INFO, +- "stopped accounting session %08X-%08X", +- sta->acct_session_id_hi, +- sta->acct_session_id_lo); ++ "stopped accounting session %016llX", ++ (unsigned long long) sta->acct_session_id); + sta->acct_session_started = 0; + } + } + + +-void accounting_sta_get_id(struct hostapd_data *hapd, +- struct sta_info *sta) ++int accounting_sta_get_id(struct hostapd_data *hapd, struct sta_info *sta) + { +- sta->acct_session_id_lo = hapd->acct_session_id_lo++; +- if (hapd->acct_session_id_lo == 0) { +- hapd->acct_session_id_hi++; +- } +- sta->acct_session_id_hi = hapd->acct_session_id_hi; ++ return radius_gen_session_id((u8 *) &sta->acct_session_id, ++ sizeof(sta->acct_session_id)); + } + + +@@ -437,12 +439,14 @@ + if (!msg) + return; + +- if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_TERMINATE_CAUSE, +- RADIUS_ACCT_TERMINATE_CAUSE_NAS_REBOOT)) +- { +- wpa_printf(MSG_INFO, "Could not add Acct-Terminate-Cause"); +- radius_msg_free(msg); +- return; ++ if (hapd->acct_session_id) { ++ char buf[20]; ++ ++ os_snprintf(buf, sizeof(buf), "%016llX", ++ (unsigned long long) hapd->acct_session_id); ++ if (!radius_msg_add_attr(msg, RADIUS_ATTR_ACCT_SESSION_ID, ++ (u8 *) buf, os_strlen(buf))) ++ wpa_printf(MSG_ERROR, "Could not add Acct-Session-Id"); + } + + if (radius_client_send(hapd->radius, msg, RADIUS_ACCT, NULL) < 0) +@@ -450,6 +454,63 @@ + } + + ++static void accounting_interim_error_cb(const u8 *addr, void *ctx) ++{ ++ struct hostapd_data *hapd = ctx; ++ struct sta_info *sta; ++ unsigned int i, wait_time; ++ int res; ++ ++ sta = ap_get_sta(hapd, addr); ++ if (!sta) ++ return; ++ sta->acct_interim_errors++; ++ if (sta->acct_interim_errors > 10 /* RADIUS_CLIENT_MAX_RETRIES */) { ++ wpa_printf(MSG_DEBUG, ++ "Interim RADIUS accounting update failed for " MACSTR ++ " - too many errors, abandon this interim accounting update", ++ MAC2STR(addr)); ++ sta->acct_interim_errors = 0; ++ /* Next update will be tried after normal update interval */ ++ return; ++ } ++ ++ /* ++ * Use a shorter update interval as an improved retransmission mechanism ++ * for failed interim accounting updates. This allows the statistics to ++ * be updated for each retransmission. ++ * ++ * RADIUS client code has already waited RADIUS_CLIENT_FIRST_WAIT. ++ * Schedule the first retry attempt immediately and every following one ++ * with exponential backoff. ++ */ ++ if (sta->acct_interim_errors == 1) { ++ wait_time = 0; ++ } else { ++ wait_time = 3; /* RADIUS_CLIENT_FIRST_WAIT */ ++ for (i = 1; i < sta->acct_interim_errors; i++) ++ wait_time *= 2; ++ } ++ res = eloop_deplete_timeout(wait_time, 0, accounting_interim_update, ++ hapd, sta); ++ if (res == 1) ++ wpa_printf(MSG_DEBUG, ++ "Interim RADIUS accounting update failed for " MACSTR ++ " (error count: %u) - schedule next update in %u seconds", ++ MAC2STR(addr), sta->acct_interim_errors, wait_time); ++ else if (res == 0) ++ wpa_printf(MSG_DEBUG, ++ "Interim RADIUS accounting update failed for " MACSTR ++ " (error count: %u)", MAC2STR(addr), ++ sta->acct_interim_errors); ++ else ++ wpa_printf(MSG_DEBUG, ++ "Interim RADIUS accounting update failed for " MACSTR ++ " (error count: %u) - no timer found", MAC2STR(addr), ++ sta->acct_interim_errors); ++} ++ ++ + /** + * accounting_init: Initialize accounting + * @hapd: hostapd BSS data +@@ -457,20 +518,15 @@ + */ + int accounting_init(struct hostapd_data *hapd) + { +- struct os_time now; ++ if (radius_gen_session_id((u8 *) &hapd->acct_session_id, ++ sizeof(hapd->acct_session_id)) < 0) ++ return -1; + +- /* Acct-Session-Id should be unique over reboots. Using a random number +- * is preferred. If that is not available, take the current time. Mix +- * in microseconds to make this more likely to be unique. */ +- os_get_time(&now); +- if (os_get_random((u8 *) &hapd->acct_session_id_hi, +- sizeof(hapd->acct_session_id_hi)) < 0) +- hapd->acct_session_id_hi = now.sec; +- hapd->acct_session_id_hi ^= now.usec; +- + if (radius_client_register(hapd->radius, RADIUS_ACCT, + accounting_receive, hapd)) + return -1; ++ radius_client_set_interim_error_cb(hapd->radius, ++ accounting_interim_error_cb, hapd); + + accounting_report_state(hapd, 1); + +--- contrib/wpa/src/ap/accounting.h.orig ++++ contrib/wpa/src/ap/accounting.h +@@ -10,9 +10,10 @@ + #define ACCOUNTING_H + + #ifdef CONFIG_NO_ACCOUNTING +-static inline void accounting_sta_get_id(struct hostapd_data *hapd, +- struct sta_info *sta) ++static inline int accounting_sta_get_id(struct hostapd_data *hapd, ++ struct sta_info *sta) + { ++ return 0; + } + + static inline void accounting_sta_start(struct hostapd_data *hapd, +@@ -34,7 +35,7 @@ + { + } + #else /* CONFIG_NO_ACCOUNTING */ +-void accounting_sta_get_id(struct hostapd_data *hapd, struct sta_info *sta); ++int accounting_sta_get_id(struct hostapd_data *hapd, struct sta_info *sta); + void accounting_sta_start(struct hostapd_data *hapd, struct sta_info *sta); + void accounting_sta_stop(struct hostapd_data *hapd, struct sta_info *sta); + int accounting_init(struct hostapd_data *hapd); +--- contrib/wpa/src/ap/acs.c.orig ++++ contrib/wpa/src/ap/acs.c +@@ -13,6 +13,7 @@ + #include "utils/common.h" + #include "utils/list.h" + #include "common/ieee802_11_defs.h" ++#include "common/hw_features_common.h" + #include "common/wpa_ctrl.h" + #include "drivers/driver.h" + #include "hostapd.h" +@@ -260,7 +261,7 @@ + } + + +-static void acs_cleanup(struct hostapd_iface *iface) ++void acs_cleanup(struct hostapd_iface *iface) + { + int i; + struct hostapd_channel_data *chan; +@@ -314,7 +315,7 @@ + + /* TODO: figure out the best multiplier for noise floor base */ + factor = pow(10, survey->nf / 5.0L) + +- (busy / total) * ++ (total ? (busy / total) : 0) * + pow(2, pow(10, (long double) survey->nf / 10.0L) - + pow(10, (long double) min_nf / 10.0L)); + +@@ -331,12 +332,10 @@ + long double int_factor = 0; + unsigned count = 0; + +- if (dl_list_empty(&chan->survey_list)) ++ if (dl_list_empty(&chan->survey_list) || ++ (chan->flag & HOSTAPD_CHAN_DISABLED)) + return; + +- if (chan->flag & HOSTAPD_CHAN_DISABLED) +- return; +- + chan->interference_factor = 0; + + dl_list_for_each(survey, &chan->survey_list, struct freq_survey, list) +@@ -359,13 +358,12 @@ + (unsigned long) survey->channel_time_rx); + } + +- if (!count) +- return; +- chan->interference_factor /= count; ++ if (count) ++ chan->interference_factor /= count; + } + + +-static int acs_usable_ht40_chan(struct hostapd_channel_data *chan) ++static int acs_usable_ht40_chan(const struct hostapd_channel_data *chan) + { + const int allowed[] = { 36, 44, 52, 60, 100, 108, 116, 124, 132, 149, + 157, 184, 192 }; +@@ -379,7 +377,7 @@ + } + + +-static int acs_usable_vht80_chan(struct hostapd_channel_data *chan) ++static int acs_usable_vht80_chan(const struct hostapd_channel_data *chan) + { + const int allowed[] = { 36, 52, 100, 116, 132, 149 }; + unsigned int i; +@@ -392,6 +390,19 @@ + } + + ++static int acs_usable_vht160_chan(const struct hostapd_channel_data *chan) ++{ ++ const int allowed[] = { 36, 100 }; ++ unsigned int i; ++ ++ for (i = 0; i < ARRAY_SIZE(allowed); i++) ++ if (chan->chan == allowed[i]) ++ return 1; ++ ++ return 0; ++} ++ ++ + static int acs_survey_is_sufficient(struct freq_survey *survey) + { + if (!(survey->filled & SURVEY_HAS_NF)) { +@@ -450,13 +461,9 @@ + + for (i = 0; i < iface->current_mode->num_channels; i++) { + chan = &iface->current_mode->channels[i]; +- if (chan->flag & HOSTAPD_CHAN_DISABLED) +- continue; +- +- if (!acs_survey_list_is_sufficient(chan)) +- continue; +- +- valid++; ++ if (!(chan->flag & HOSTAPD_CHAN_DISABLED) && ++ acs_survey_list_is_sufficient(chan)) ++ valid++; + } + + /* We need at least survey data for one channel */ +@@ -466,13 +473,9 @@ + + static int acs_usable_chan(struct hostapd_channel_data *chan) + { +- if (dl_list_empty(&chan->survey_list)) +- return 0; +- if (chan->flag & HOSTAPD_CHAN_DISABLED) +- return 0; +- if (!acs_survey_list_is_sufficient(chan)) +- return 0; +- return 1; ++ return !dl_list_empty(&chan->survey_list) && ++ !(chan->flag & HOSTAPD_CHAN_DISABLED) && ++ acs_survey_list_is_sufficient(chan); + } + + +@@ -576,6 +579,7 @@ + long double factor, ideal_factor = 0; + int i, j; + int n_chans = 1; ++ u32 bw; + unsigned int k; + + /* TODO: HT40- support */ +@@ -590,18 +594,24 @@ + iface->conf->secondary_channel) + n_chans = 2; + +- if (iface->conf->ieee80211ac && +- iface->conf->vht_oper_chwidth == 1) +- n_chans = 4; ++ if (iface->conf->ieee80211ac) { ++ switch (iface->conf->vht_oper_chwidth) { ++ case VHT_CHANWIDTH_80MHZ: ++ n_chans = 4; ++ break; ++ case VHT_CHANWIDTH_160MHZ: ++ n_chans = 8; ++ break; ++ } ++ } + +- /* TODO: VHT80+80, VHT160. Update acs_adjust_vht_center_freq() too. */ ++ bw = num_chan_to_bw(n_chans); + +- wpa_printf(MSG_DEBUG, "ACS: Survey analysis for selected bandwidth %d MHz", +- n_chans == 1 ? 20 : +- n_chans == 2 ? 40 : +- n_chans == 4 ? 80 : +- -1); ++ /* TODO: VHT80+80. Update acs_adjust_vht_center_freq() too. */ + ++ wpa_printf(MSG_DEBUG, ++ "ACS: Survey analysis for selected bandwidth %d MHz", bw); ++ + for (i = 0; i < iface->current_mode->num_channels; i++) { + double total_weight; + struct acs_bias *bias, tmp_bias; +@@ -608,12 +618,23 @@ + + chan = &iface->current_mode->channels[i]; + +- if (chan->flag & HOSTAPD_CHAN_DISABLED) ++ /* Since in the current ACS implementation the first channel is ++ * always a primary channel, skip channels not available as ++ * primary until more sophisticated channel selection is ++ * implemented. */ ++ if (!chan_pri_allowed(chan)) + continue; + + if (!is_in_chanlist(iface, chan)) + continue; + ++ if (!chan_bw_allowed(chan, bw, 1, 1)) { ++ wpa_printf(MSG_DEBUG, ++ "ACS: Channel %d: BW %u is not supported", ++ chan->chan, bw); ++ continue; ++ } ++ + /* HT40 on 5 GHz has a limited set of primary channels as per + * 11n Annex J */ + if (iface->current_mode->mode == HOSTAPD_MODE_IEEE80211A && +@@ -626,12 +647,24 @@ + } + + if (iface->current_mode->mode == HOSTAPD_MODE_IEEE80211A && +- iface->conf->ieee80211ac && +- iface->conf->vht_oper_chwidth == 1 && +- !acs_usable_vht80_chan(chan)) { +- wpa_printf(MSG_DEBUG, "ACS: Channel %d: not allowed as primary channel for VHT80", +- chan->chan); +- continue; ++ iface->conf->ieee80211ac) { ++ if (iface->conf->vht_oper_chwidth == ++ VHT_CHANWIDTH_80MHZ && ++ !acs_usable_vht80_chan(chan)) { ++ wpa_printf(MSG_DEBUG, ++ "ACS: Channel %d: not allowed as primary channel for VHT80", ++ chan->chan); ++ continue; ++ } ++ ++ if (iface->conf->vht_oper_chwidth == ++ VHT_CHANWIDTH_160MHZ && ++ !acs_usable_vht160_chan(chan)) { ++ wpa_printf(MSG_DEBUG, ++ "ACS: Channel %d: not allowed as primary channel for VHT160", ++ chan->chan); ++ continue; ++ } + } + + factor = 0; +@@ -644,6 +677,13 @@ + if (!adj_chan) + break; + ++ if (!chan_bw_allowed(adj_chan, bw, 1, 0)) { ++ wpa_printf(MSG_DEBUG, ++ "ACS: PRI Channel %d: secondary channel %d BW %u is not supported", ++ chan->chan, adj_chan->chan, bw); ++ break; ++ } ++ + if (acs_usable_chan(adj_chan)) { + factor += adj_chan->interference_factor; + total_weight += 1; +@@ -756,10 +796,14 @@ + case VHT_CHANWIDTH_80MHZ: + offset = 6; + break; ++ case VHT_CHANWIDTH_160MHZ: ++ offset = 14; ++ break; + default: + /* TODO: How can this be calculated? Adjust + * acs_find_ideal_chan() */ +- wpa_printf(MSG_INFO, "ACS: Only VHT20/40/80 is supported now"); ++ wpa_printf(MSG_INFO, ++ "ACS: Only VHT20/40/80/160 is supported now"); + return; + } + +@@ -789,10 +833,7 @@ + + static int acs_study_options(struct hostapd_iface *iface) + { +- int err; +- +- err = acs_study_survey_based(iface); +- if (err == 0) ++ if (acs_study_survey_based(iface) == 0) + return 0; + + /* TODO: If no surveys are available/sufficient this is a good +@@ -921,22 +962,21 @@ + + enum hostapd_chan_status acs_init(struct hostapd_iface *iface) + { +- int err; +- + wpa_printf(MSG_INFO, "ACS: Automatic channel selection started, this may take a bit"); + + if (iface->drv_flags & WPA_DRIVER_FLAGS_ACS_OFFLOAD) { + wpa_printf(MSG_INFO, "ACS: Offloading to driver"); +- err = hostapd_drv_do_acs(iface->bss[0]); +- if (err) ++ if (hostapd_drv_do_acs(iface->bss[0])) + return HOSTAPD_CHAN_INVALID; + return HOSTAPD_CHAN_ACS; + } + ++ if (!iface->current_mode) ++ return HOSTAPD_CHAN_INVALID; ++ + acs_cleanup(iface); + +- err = acs_request_scan(iface); +- if (err < 0) ++ if (acs_request_scan(iface) < 0) + return HOSTAPD_CHAN_INVALID; + + hostapd_set_state(iface, HAPD_IFACE_ACS); +--- contrib/wpa/src/ap/acs.h.orig ++++ contrib/wpa/src/ap/acs.h +@@ -13,6 +13,7 @@ + #ifdef CONFIG_ACS + + enum hostapd_chan_status acs_init(struct hostapd_iface *iface); ++void acs_cleanup(struct hostapd_iface *iface); + + #else /* CONFIG_ACS */ + +@@ -22,6 +23,10 @@ + return HOSTAPD_CHAN_INVALID; + } + ++static inline void acs_cleanup(struct hostapd_iface *iface) ++{ ++} ++ + #endif /* CONFIG_ACS */ + + #endif /* ACS_H */ +--- contrib/wpa/src/ap/ap_config.c.orig ++++ contrib/wpa/src/ap/ap_config.c +@@ -10,9 +10,11 @@ + + #include "utils/common.h" + #include "crypto/sha1.h" ++#include "crypto/tls.h" + #include "radius/radius_client.h" + #include "common/ieee802_11_defs.h" + #include "common/eapol_common.h" ++#include "common/dhcp.h" + #include "eap_common/eap_wsc_common.h" + #include "eap_server/eap.h" + #include "wpa_auth.h" +@@ -36,8 +38,14 @@ + } + + ++#ifndef DEFAULT_WPA_DISABLE_EAPOL_KEY_RETRIES ++#define DEFAULT_WPA_DISABLE_EAPOL_KEY_RETRIES 0 ++#endif /* DEFAULT_WPA_DISABLE_EAPOL_KEY_RETRIES */ ++ + void hostapd_config_defaults_bss(struct hostapd_bss_config *bss) + { ++ dl_list_init(&bss->anqp_elem); ++ + bss->logger_syslog_level = HOSTAPD_LEVEL_INFO; + bss->logger_stdout_level = HOSTAPD_LEVEL_INFO; + bss->logger_syslog = (unsigned int) -1; +@@ -53,6 +61,10 @@ + + bss->wpa_group_rekey = 600; + bss->wpa_gmk_rekey = 86400; ++ bss->wpa_group_update_count = 4; ++ bss->wpa_pairwise_update_count = 4; ++ bss->wpa_disable_eapol_key_retries = ++ DEFAULT_WPA_DISABLE_EAPOL_KEY_RETRIES; + bss->wpa_key_mgmt = WPA_KEY_MGMT_PSK; + bss->wpa_pairwise = WPA_CIPHER_TKIP; + bss->wpa_group = WPA_CIPHER_TKIP; +@@ -63,6 +75,7 @@ + bss->dtim_period = 2; + + bss->radius_server_auth_port = 1812; ++ bss->eap_sim_db_timeout = 1; + bss->ap_max_inactivity = AP_MAX_INACTIVITY; + bss->eapol_version = EAPOL_VERSION; + +@@ -85,13 +98,48 @@ + /* Set to -1 as defaults depends on HT in setup */ + bss->wmm_enabled = -1; + +-#ifdef CONFIG_IEEE80211R ++#ifdef CONFIG_IEEE80211R_AP + bss->ft_over_ds = 1; +-#endif /* CONFIG_IEEE80211R */ ++ bss->rkh_pos_timeout = 86400; ++ bss->rkh_neg_timeout = 60; ++ bss->rkh_pull_timeout = 1000; ++ bss->rkh_pull_retries = 4; ++ bss->r0_key_lifetime = 1209600; ++#endif /* CONFIG_IEEE80211R_AP */ + + bss->radius_das_time_window = 300; + + bss->sae_anti_clogging_threshold = 5; ++ bss->sae_sync = 5; ++ ++ bss->gas_frag_limit = 1400; ++ ++#ifdef CONFIG_FILS ++ dl_list_init(&bss->fils_realms); ++ bss->fils_hlp_wait_time = 30; ++ bss->dhcp_server_port = DHCP_SERVER_PORT; ++ bss->dhcp_relay_port = DHCP_SERVER_PORT; ++#endif /* CONFIG_FILS */ ++ ++ bss->broadcast_deauth = 1; ++ ++#ifdef CONFIG_MBO ++ bss->mbo_cell_data_conn_pref = -1; ++#endif /* CONFIG_MBO */ ++ ++ /* Disable TLS v1.3 by default for now to avoid interoperability issue. ++ * This can be enabled by default once the implementation has been fully ++ * completed and tested with other implementations. */ ++ bss->tls_flags = TLS_CONN_DISABLE_TLSv1_3; ++ ++ bss->send_probe_response = 1; ++ ++#ifdef CONFIG_HS20 ++ bss->hs20_release = (HS20_VERSION >> 4) + 1; ++#endif /* CONFIG_HS20 */ ++ ++ /* Default to strict CRL checking. */ ++ bss->check_crl_strict = 1; + } + + +@@ -152,9 +200,8 @@ + conf->num_bss = 1; + + conf->beacon_int = 100; +- conf->rts_threshold = -1; /* use driver default: 2347 */ +- conf->fragm_threshold = -1; /* user driver default: 2346 */ +- conf->send_probe_response = 1; ++ conf->rts_threshold = -2; /* use driver default: 2347 */ ++ conf->fragm_threshold = -2; /* user driver default: 2346 */ + /* Set to invalid value means do not add Power Constraint IE */ + conf->local_pwr_constraint = -1; + +@@ -180,6 +227,7 @@ + conf->ignore_assoc_probability = 0.0; + conf->ignore_reassoc_probability = 0.0; + conf->corrupt_gtk_rekey_mic_probability = 0.0; ++ conf->ecsa_ie_only = 0; + #endif /* CONFIG_TESTING_OPTIONS */ + + conf->acs = 0; +@@ -188,6 +236,14 @@ + conf->acs_num_scans = 5; + #endif /* CONFIG_ACS */ + ++ /* The third octet of the country string uses an ASCII space character ++ * by default to indicate that the regulations encompass all ++ * environments for the current frequency band in the country. */ ++ conf->country[2] = ' '; ++ ++ conf->rssi_reject_assoc_rssi = 0; ++ conf->rssi_reject_assoc_timeout = 30; ++ + return conf; + } + +@@ -198,18 +254,17 @@ + } + + +-int hostapd_mac_comp_empty(const void *a) +-{ +- macaddr empty = { 0 }; +- return os_memcmp(a, empty, sizeof(macaddr)); +-} +- +- + static int hostapd_config_read_wpa_psk(const char *fname, + struct hostapd_ssid *ssid) + { + FILE *f; + char buf[128], *pos; ++ const char *keyid; ++ char *context; ++ char *context2; ++ char *token; ++ char *name; ++ char *value; + int line = 0, ret = 0, len, ok; + u8 addr[ETH_ALEN]; + struct hostapd_wpa_psk *psk; +@@ -224,6 +279,8 @@ + } + + while (fgets(buf, sizeof(buf), f)) { ++ int vlan_id = 0; ++ + line++; + + if (buf[0] == '#') +@@ -239,9 +296,39 @@ + if (buf[0] == '\0') + continue; + +- if (hwaddr_aton(buf, addr)) { ++ context = NULL; ++ keyid = NULL; ++ while ((token = str_token(buf, " ", &context))) { ++ if (!os_strchr(token, '=')) ++ break; ++ context2 = NULL; ++ name = str_token(token, "=", &context2); ++ if (!name) ++ break; ++ value = str_token(token, "", &context2); ++ if (!value) ++ value = ""; ++ if (!os_strcmp(name, "keyid")) { ++ keyid = value; ++ } else if (!os_strcmp(name, "vlanid")) { ++ vlan_id = atoi(value); ++ } else { ++ wpa_printf(MSG_ERROR, ++ "Unrecognized '%s=%s' on line %d in '%s'", ++ name, value, line, fname); ++ ret = -1; ++ break; ++ } ++ } ++ ++ if (ret == -1) ++ break; ++ ++ if (!token) ++ token = ""; ++ if (hwaddr_aton(token, addr)) { + wpa_printf(MSG_ERROR, "Invalid MAC address '%s' on " +- "line %d in '%s'", buf, line, fname); ++ "line %d in '%s'", token, line, fname); + ret = -1; + break; + } +@@ -252,13 +339,14 @@ + ret = -1; + break; + } ++ psk->vlan_id = vlan_id; + if (is_zero_ether_addr(addr)) + psk->group = 1; + else + os_memcpy(psk->addr, addr, ETH_ALEN); + +- pos = buf + 17; +- if (*pos == '\0') { ++ pos = str_token(buf, "", &context); ++ if (!pos) { + wpa_printf(MSG_ERROR, "No PSK on line %d in '%s'", + line, fname); + os_free(psk); +@@ -265,7 +353,6 @@ + ret = -1; + break; + } +- pos++; + + ok = 0; + len = os_strlen(pos); +@@ -284,6 +371,18 @@ + break; + } + ++ if (keyid) { ++ len = os_strlcpy(psk->keyid, keyid, sizeof(psk->keyid)); ++ if ((size_t) len >= sizeof(psk->keyid)) { ++ wpa_printf(MSG_ERROR, ++ "PSK keyid too long on line %d in '%s'", ++ line, fname); ++ os_free(psk); ++ ret = -1; ++ break; ++ } ++ } ++ + psk->next = ssid->wpa_psk; + ssid->wpa_psk = psk; + } +@@ -332,13 +431,7 @@ + ssid->wpa_psk->group = 1; + } + +- if (ssid->wpa_psk_file) { +- if (hostapd_config_read_wpa_psk(ssid->wpa_psk_file, +- &conf->ssid)) +- return -1; +- } +- +- return 0; ++ return hostapd_config_read_wpa_psk(ssid->wpa_psk_file, &conf->ssid); + } + + +@@ -383,10 +476,23 @@ + hostapd_config_free_radius_attr(user->accept_attr); + os_free(user->identity); + bin_clear_free(user->password, user->password_len); ++ bin_clear_free(user->salt, user->salt_len); + os_free(user); + } + + ++void hostapd_config_free_eap_users(struct hostapd_eap_user *user) ++{ ++ struct hostapd_eap_user *prev_user; ++ ++ while (user) { ++ prev_user = user; ++ user = user->next; ++ hostapd_config_free_eap_user(prev_user); ++ } ++} ++ ++ + static void hostapd_config_free_wep(struct hostapd_wep_keys *keys) + { + int i; +@@ -410,10 +516,51 @@ + } + + ++static void hostapd_config_free_anqp_elem(struct hostapd_bss_config *conf) ++{ ++ struct anqp_element *elem; ++ ++ while ((elem = dl_list_first(&conf->anqp_elem, struct anqp_element, ++ list))) { ++ dl_list_del(&elem->list); ++ wpabuf_free(elem->payload); ++ os_free(elem); ++ } ++} ++ ++ ++static void hostapd_config_free_fils_realms(struct hostapd_bss_config *conf) ++{ ++#ifdef CONFIG_FILS ++ struct fils_realm *realm; ++ ++ while ((realm = dl_list_first(&conf->fils_realms, struct fils_realm, ++ list))) { ++ dl_list_del(&realm->list); ++ os_free(realm); ++ } ++#endif /* CONFIG_FILS */ ++} ++ ++ ++static void hostapd_config_free_sae_passwords(struct hostapd_bss_config *conf) ++{ ++ struct sae_password_entry *pw, *tmp; ++ ++ pw = conf->sae_passwords; ++ conf->sae_passwords = NULL; ++ while (pw) { ++ tmp = pw; ++ pw = pw->next; ++ str_clear_free(tmp->password); ++ os_free(tmp->identifier); ++ os_free(tmp); ++ } ++} ++ ++ + void hostapd_config_free_bss(struct hostapd_bss_config *conf) + { +- struct hostapd_eap_user *user, *prev_user; +- + if (conf == NULL) + return; + +@@ -426,12 +573,7 @@ + os_free(conf->ssid.vlan_tagged_interface); + #endif /* CONFIG_FULL_DYNAMIC_VLAN */ + +- user = conf->eap_user; +- while (user) { +- prev_user = user; +- user = user->next; +- hostapd_config_free_eap_user(prev_user); +- } ++ hostapd_config_free_eap_users(conf->eap_user); + os_free(conf->eap_user_sqlite); + + os_free(conf->eap_req_id_text); +@@ -453,9 +595,12 @@ + os_free(conf->server_cert); + os_free(conf->private_key); + os_free(conf->private_key_passwd); ++ os_free(conf->check_cert_subject); + os_free(conf->ocsp_stapling_response); ++ os_free(conf->ocsp_stapling_response_multi); + os_free(conf->dh_file); + os_free(conf->openssl_ciphers); ++ os_free(conf->openssl_ecdh_curves); + os_free(conf->pac_opaque_encr_key); + os_free(conf->eap_fast_a_id); + os_free(conf->eap_fast_a_id_info); +@@ -466,7 +611,7 @@ + hostapd_config_free_vlan(conf); + os_free(conf->time_zone); + +-#ifdef CONFIG_IEEE80211R ++#ifdef CONFIG_IEEE80211R_AP + { + struct ft_remote_r0kh *r0kh, *r0kh_prev; + struct ft_remote_r1kh *r1kh, *r1kh_prev; +@@ -487,7 +632,7 @@ + os_free(r1kh_prev); + } + } +-#endif /* CONFIG_IEEE80211R */ ++#endif /* CONFIG_IEEE80211R_AP */ + + #ifdef CONFIG_WPS + os_free(conf->wps_pin_requests); +@@ -500,6 +645,8 @@ + os_free(conf->ap_pin); + os_free(conf->extra_cred); + os_free(conf->ap_settings); ++ hostapd_config_clear_wpa_psk(&conf->multi_ap_backhaul_ssid.wpa_psk); ++ str_clear_free(conf->multi_ap_backhaul_ssid.wpa_passphrase); + os_free(conf->upnp_iface); + os_free(conf->friendly_name); + os_free(conf->manufacturer_url); +@@ -519,10 +666,12 @@ + + os_free(conf->roaming_consortium); + os_free(conf->venue_name); ++ os_free(conf->venue_url); + os_free(conf->nai_realm_data); + os_free(conf->network_auth_type); + os_free(conf->anqp_3gpp_cell_net); + os_free(conf->domain_name); ++ hostapd_config_free_anqp_elem(conf); + + #ifdef CONFIG_RADIUS_TEST + os_free(conf->dump_msk_file); +@@ -547,16 +696,31 @@ + os_free(p->icons[j]); + os_free(p->icons); + os_free(p->osu_nai); ++ os_free(p->osu_nai2); + os_free(p->service_desc); + } + os_free(conf->hs20_osu_providers); + } ++ if (conf->hs20_operator_icon) { ++ size_t i; ++ ++ for (i = 0; i < conf->hs20_operator_icon_count; i++) ++ os_free(conf->hs20_operator_icon[i]); ++ os_free(conf->hs20_operator_icon); ++ } + os_free(conf->subscr_remediation_url); ++ os_free(conf->hs20_sim_provisioning_url); ++ os_free(conf->t_c_filename); ++ os_free(conf->t_c_server_url); + #endif /* CONFIG_HS20 */ + + wpabuf_free(conf->vendor_elements); ++ wpabuf_free(conf->assocresp_elements); + + os_free(conf->sae_groups); ++#ifdef CONFIG_OWE ++ os_free(conf->owe_groups); ++#endif /* CONFIG_OWE */ + + os_free(conf->wowlan_triggers); + +@@ -564,11 +728,22 @@ + + #ifdef CONFIG_TESTING_OPTIONS + wpabuf_free(conf->own_ie_override); ++ wpabuf_free(conf->sae_commit_override); + #endif /* CONFIG_TESTING_OPTIONS */ + + os_free(conf->no_probe_resp_if_seen_on); + os_free(conf->no_auth_if_seen_on); + ++ hostapd_config_free_fils_realms(conf); ++ ++#ifdef CONFIG_DPP ++ os_free(conf->dpp_connector); ++ wpabuf_free(conf->dpp_netaccesskey); ++ wpabuf_free(conf->dpp_csign); ++#endif /* CONFIG_DPP */ ++ ++ hostapd_config_free_sae_passwords(conf); ++ + os_free(conf); + } + +@@ -594,6 +769,8 @@ + #ifdef CONFIG_ACS + os_free(conf->acs_chan_bias); + #endif /* CONFIG_ACS */ ++ wpabuf_free(conf->lci); ++ wpabuf_free(conf->civic); + + os_free(conf); + } +@@ -610,7 +787,7 @@ + * Perform a binary search for given MAC address from a pre-sorted list. + */ + int hostapd_maclist_found(struct mac_acl_entry *list, int num_entries, +- const u8 *addr, int *vlan_id) ++ const u8 *addr, struct vlan_description *vlan_id) + { + int start, end, middle, res; + +@@ -650,11 +827,26 @@ + } + + +-int hostapd_vlan_id_valid(struct hostapd_vlan *vlan, int vlan_id) ++int hostapd_vlan_valid(struct hostapd_vlan *vlan, ++ struct vlan_description *vlan_desc) + { + struct hostapd_vlan *v = vlan; ++ int i; ++ ++ if (!vlan_desc->notempty || vlan_desc->untagged < 0 || ++ vlan_desc->untagged > MAX_VLAN_ID) ++ return 0; ++ for (i = 0; i < MAX_NUM_TAGGED_VLAN; i++) { ++ if (vlan_desc->tagged[i] < 0 || ++ vlan_desc->tagged[i] > MAX_VLAN_ID) ++ return 0; ++ } ++ if (!vlan_desc->untagged && !vlan_desc->tagged[0]) ++ return 0; ++ + while (v) { +- if (v->vlan_id == vlan_id || v->vlan_id == VLAN_ID_WILDCARD) ++ if (!vlan_compare(&v->vlan_desc, vlan_desc) || ++ v->vlan_id == VLAN_ID_WILDCARD) + return 1; + v = v->next; + } +@@ -676,11 +868,14 @@ + + const u8 * hostapd_get_psk(const struct hostapd_bss_config *conf, + const u8 *addr, const u8 *p2p_dev_addr, +- const u8 *prev_psk) ++ const u8 *prev_psk, int *vlan_id) + { + struct hostapd_wpa_psk *psk; + int next_ok = prev_psk == NULL; + ++ if (vlan_id) ++ *vlan_id = 0; ++ + if (p2p_dev_addr && !is_zero_ether_addr(p2p_dev_addr)) { + wpa_printf(MSG_DEBUG, "Searching a PSK for " MACSTR + " p2p_dev_addr=" MACSTR " prev_psk=%p", +@@ -698,8 +893,11 @@ + (addr && os_memcmp(psk->addr, addr, ETH_ALEN) == 0) || + (!addr && p2p_dev_addr && + os_memcmp(psk->p2p_dev_addr, p2p_dev_addr, ETH_ALEN) == +- 0))) ++ 0))) { ++ if (vlan_id) ++ *vlan_id = psk->vlan_id; + return psk->psk; ++ } + + if (psk->psk == prev_psk) + next_ok = 1; +@@ -756,7 +954,7 @@ + return -1; + } + +- if (full_config && hostapd_mac_comp_empty(bss->bssid) != 0) { ++ if (full_config && !is_zero_ether_addr(bss->bssid)) { + size_t i; + + for (i = 0; i < conf->num_bss; i++) { +@@ -772,7 +970,7 @@ + } + } + +-#ifdef CONFIG_IEEE80211R ++#ifdef CONFIG_IEEE80211R_AP + if (full_config && wpa_key_mgmt_ft(bss->wpa_key_mgmt) && + (bss->nas_identifier == NULL || + os_strlen(bss->nas_identifier) < 1 || +@@ -782,7 +980,7 @@ + "string"); + return -1; + } +-#endif /* CONFIG_IEEE80211R */ ++#endif /* CONFIG_IEEE80211R_AP */ + + #ifdef CONFIG_IEEE80211N + if (full_config && conf->ieee80211n && +@@ -811,6 +1009,25 @@ + } + #endif /* CONFIG_IEEE80211N */ + ++#ifdef CONFIG_IEEE80211AC ++ if (full_config && conf->ieee80211ac && ++ bss->ssid.security_policy == SECURITY_STATIC_WEP) { ++ bss->disable_11ac = 1; ++ wpa_printf(MSG_ERROR, ++ "VHT (IEEE 802.11ac) with WEP is not allowed, disabling VHT capabilities"); ++ } ++ ++ if (full_config && conf->ieee80211ac && bss->wpa && ++ !(bss->wpa_pairwise & WPA_CIPHER_CCMP) && ++ !(bss->rsn_pairwise & (WPA_CIPHER_CCMP | WPA_CIPHER_GCMP | ++ WPA_CIPHER_CCMP_256 | WPA_CIPHER_GCMP_256))) ++ { ++ bss->disable_11ac = 1; ++ wpa_printf(MSG_ERROR, ++ "VHT (IEEE 802.11ac) with WPA/WPA2 requires CCMP/GCMP to be enabled, disabling VHT capabilities"); ++ } ++#endif /* CONFIG_IEEE80211AC */ ++ + #ifdef CONFIG_WPS + if (full_config && bss->wps_state && bss->ignore_broadcast_ssid) { + wpa_printf(MSG_INFO, "WPS: ignore_broadcast_ssid " +@@ -827,7 +1044,9 @@ + + if (full_config && bss->wps_state && bss->wpa && + (!(bss->wpa & 2) || +- !(bss->rsn_pairwise & (WPA_CIPHER_CCMP | WPA_CIPHER_GCMP)))) { ++ !(bss->rsn_pairwise & (WPA_CIPHER_CCMP | WPA_CIPHER_GCMP | ++ WPA_CIPHER_CCMP_256 | ++ WPA_CIPHER_GCMP_256)))) { + wpa_printf(MSG_INFO, "WPS: WPA/TKIP configuration without " + "WPA2/CCMP/GCMP forced WPS to be disabled"); + bss->wps_state = 0; +@@ -847,6 +1066,24 @@ + } + #endif /* CONFIG_HS20 */ + ++#ifdef CONFIG_MBO ++ if (full_config && bss->mbo_enabled && (bss->wpa & 2) && ++ bss->ieee80211w == NO_MGMT_FRAME_PROTECTION) { ++ wpa_printf(MSG_ERROR, ++ "MBO: PMF needs to be enabled whenever using WPA2 with MBO"); ++ return -1; ++ } ++#endif /* CONFIG_MBO */ ++ ++#ifdef CONFIG_OCV ++ if (full_config && bss->ieee80211w == NO_MGMT_FRAME_PROTECTION && ++ bss->ocv) { ++ wpa_printf(MSG_ERROR, ++ "OCV: PMF needs to be enabled whenever using OCV"); ++ return -1; ++ } ++#endif /* CONFIG_OCV */ ++ + return 0; + } + +@@ -928,8 +1165,15 @@ + + if ((bss->wpa & 2) && bss->rsn_pairwise == 0) + bss->rsn_pairwise = bss->wpa_pairwise; +- bss->wpa_group = wpa_select_ap_group_cipher(bss->wpa, bss->wpa_pairwise, +- bss->rsn_pairwise); ++ if (bss->group_cipher) ++ bss->wpa_group = bss->group_cipher; ++ else ++ bss->wpa_group = wpa_select_ap_group_cipher(bss->wpa, ++ bss->wpa_pairwise, ++ bss->rsn_pairwise); ++ if (!bss->wpa_group_rekey_set) ++ bss->wpa_group_rekey = bss->wpa_group == WPA_CIPHER_TKIP ? ++ 600 : 86400; + + if (full_config) { + bss->radius->auth_server = bss->radius->auth_servers; +@@ -983,3 +1227,26 @@ + } + } + } ++ ++ ++int hostapd_sae_pw_id_in_use(struct hostapd_bss_config *conf) ++{ ++ int with_id = 0, without_id = 0; ++ struct sae_password_entry *pw; ++ ++ if (conf->ssid.wpa_passphrase) ++ without_id = 1; ++ ++ for (pw = conf->sae_passwords; pw; pw = pw->next) { ++ if (pw->identifier) ++ with_id = 1; ++ else ++ without_id = 1; ++ if (with_id && without_id) ++ break; ++ } ++ ++ if (with_id && !without_id) ++ return 2; ++ return with_id; ++} +--- contrib/wpa/src/ap/ap_config.h.orig ++++ contrib/wpa/src/ap/ap_config.h +@@ -10,6 +10,7 @@ + #define HOSTAPD_CONFIG_H + + #include "common/defs.h" ++#include "utils/list.h" + #include "ip_addr.h" + #include "common/wpa_common.h" + #include "common/ieee802_11_defs.h" +@@ -16,6 +17,7 @@ + #include "common/ieee802_11_common.h" + #include "wps/wps.h" + #include "fst/fst.h" ++#include "vlan.h" + + /** + * mesh_conf - local MBSS state and settings +@@ -39,6 +41,11 @@ + #define MESH_CONF_SEC_AUTH BIT(1) + #define MESH_CONF_SEC_AMPE BIT(2) + unsigned int security; ++ enum mfp_options ieee80211w; ++ int ocv; ++ unsigned int pairwise_cipher; ++ unsigned int group_cipher; ++ unsigned int mgmt_group_cipher; + int dot11MeshMaxRetries; + int dot11MeshRetryTimeout; /* msec */ + int dot11MeshConfirmTimeout; /* msec */ +@@ -52,7 +59,7 @@ + + struct mac_acl_entry { + macaddr addr; +- int vlan_id; ++ struct vlan_description vlan_id; + }; + + struct hostapd_radius_servers; +@@ -102,6 +109,7 @@ + #define DYNAMIC_VLAN_NAMING_WITH_DEVICE 1 + #define DYNAMIC_VLAN_NAMING_END 2 + int vlan_naming; ++ int per_sta_vif; + #ifdef CONFIG_FULL_DYNAMIC_VLAN + char *vlan_tagged_interface; + #endif /* CONFIG_FULL_DYNAMIC_VLAN */ +@@ -113,7 +121,9 @@ + struct hostapd_vlan { + struct hostapd_vlan *next; + int vlan_id; /* VLAN ID or -1 (VLAN_ID_WILDCARD) for wildcard entry */ ++ struct vlan_description vlan_desc; + char ifname[IFNAMSIZ + 1]; ++ char bridge[IFNAMSIZ + 1]; + int configured; + int dynamic_vlan; + #ifdef CONFIG_FULL_DYNAMIC_VLAN +@@ -124,17 +134,25 @@ + }; + + #define PMK_LEN 32 ++#define KEYID_LEN 32 ++#define MIN_PASSPHRASE_LEN 8 ++#define MAX_PASSPHRASE_LEN 63 + struct hostapd_sta_wpa_psk_short { + struct hostapd_sta_wpa_psk_short *next; ++ unsigned int is_passphrase:1; + u8 psk[PMK_LEN]; ++ char passphrase[MAX_PASSPHRASE_LEN + 1]; ++ int ref; /* (number of references held) - 1 */ + }; + + struct hostapd_wpa_psk { + struct hostapd_wpa_psk *next; + int group; ++ char keyid[KEYID_LEN]; + u8 psk[PMK_LEN]; + u8 addr[ETH_ALEN]; + u8 p2p_dev_addr[ETH_ALEN]; ++ int vlan_id; + }; + + struct hostapd_eap_user { +@@ -147,6 +165,8 @@ + } methods[EAP_MAX_METHODS]; + u8 *password; + size_t password_len; ++ u8 *salt; ++ size_t salt_len; /* non-zero when password is salted */ + int phase2; + int force_version; + unsigned int wildcard_prefix:1; +@@ -156,6 +176,7 @@ + unsigned int macacl:1; + int ttls_auth; /* EAP_TTLS_AUTH_* bitfield */ + struct hostapd_radius_attr *accept_attr; ++ u32 t_c_timestamp; + }; + + struct hostapd_radius_attr { +@@ -188,6 +209,12 @@ + u8 name[252]; + }; + ++struct hostapd_venue_url { ++ u8 venue_number; ++ u8 url_len; ++ u8 url[254]; ++}; ++ + #define MAX_NAI_REALMS 10 + #define MAX_NAI_REALMLEN 255 + #define MAX_NAI_EAP_METHODS 5 +@@ -205,6 +232,26 @@ + } eap_method[MAX_NAI_EAP_METHODS]; + }; + ++struct anqp_element { ++ struct dl_list list; ++ u16 infoid; ++ struct wpabuf *payload; ++}; ++ ++struct fils_realm { ++ struct dl_list list; ++ u8 hash[2]; ++ char realm[]; ++}; ++ ++struct sae_password_entry { ++ struct sae_password_entry *next; ++ char *password; ++ char *identifier; ++ u8 peer_addr[ETH_ALEN]; ++ int vlan_id; ++}; ++ + /** + * struct hostapd_bss_config - Per-BSS configuration + */ +@@ -222,7 +269,8 @@ + int max_num_sta; /* maximum number of STAs in station table */ + + int dtim_period; +- int bss_load_update_period; ++ unsigned int bss_load_update_period; ++ unsigned int chan_util_avg_period; + + int ieee802_1x; /* use IEEE 802.1X */ + int eapol_version; +@@ -231,6 +279,7 @@ + struct hostapd_eap_user *eap_user; + char *eap_user_sqlite; + char *eap_sim_db; ++ unsigned int eap_sim_db_timeout; + int eap_server_erp; /* Whether ERP is enabled on internal EAP server */ + struct hostapd_ip_addr own_ip_addr; + char *nas_identifier; +@@ -242,6 +291,7 @@ + int radius_das_port; + unsigned int radius_das_time_window; + int radius_das_require_event_timestamp; ++ int radius_das_require_message_authenticator; + struct hostapd_ip_addr radius_das_client_addr; + u8 *radius_das_shared_secret; + size_t radius_das_shared_secret_len; +@@ -265,7 +315,7 @@ + char iapp_iface[IFNAMSIZ + 1]; /* interface used with IAPP broadcast + * frames */ + +- enum { ++ enum macaddr_acl { + ACCEPT_UNLESS_DENIED = 0, + DENY_UNLESS_ACCEPTED = 1, + USE_EXTERNAL_RADIUS_AUTH = 2 +@@ -291,6 +341,9 @@ + /* dot11AssociationSAQueryRetryTimeout (in TUs) */ + int assoc_sa_query_retry_timeout; + #endif /* CONFIG_IEEE80211W */ ++#ifdef CONFIG_OCV ++ int ocv; /* Operating Channel Validation */ ++#endif /* CONFIG_OCV */ + enum { + PSK_RADIUS_IGNORED = 0, + PSK_RADIUS_ACCEPTED = 1, +@@ -297,27 +350,37 @@ + PSK_RADIUS_REQUIRED = 2 + } wpa_psk_radius; + int wpa_pairwise; ++ int group_cipher; /* wpa_group value override from configuation */ + int wpa_group; + int wpa_group_rekey; ++ int wpa_group_rekey_set; + int wpa_strict_rekey; + int wpa_gmk_rekey; + int wpa_ptk_rekey; ++ u32 wpa_group_update_count; ++ u32 wpa_pairwise_update_count; ++ int wpa_disable_eapol_key_retries; + int rsn_pairwise; + int rsn_preauth; + char *rsn_preauth_interfaces; +- int peerkey; + +-#ifdef CONFIG_IEEE80211R ++#ifdef CONFIG_IEEE80211R_AP + /* IEEE 802.11r - Fast BSS Transition */ + u8 mobility_domain[MOBILITY_DOMAIN_ID_LEN]; + u8 r1_key_holder[FT_R1KH_ID_LEN]; +- u32 r0_key_lifetime; ++ u32 r0_key_lifetime; /* PMK-R0 lifetime seconds */ ++ int rkh_pos_timeout; ++ int rkh_neg_timeout; ++ int rkh_pull_timeout; /* ms */ ++ int rkh_pull_retries; + 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 ft_psk_generate_local; ++ int r1_max_key_lifetime; ++#endif /* CONFIG_IEEE80211R_AP */ + + char *ctrl_interface; /* directory for UNIX domain sockets */ + #ifndef CONFIG_NATIVE_WINDOWS +@@ -329,11 +392,17 @@ + char *server_cert; + char *private_key; + char *private_key_passwd; ++ char *check_cert_subject; + int check_crl; ++ int check_crl_strict; ++ unsigned int crl_reload_interval; + unsigned int tls_session_lifetime; ++ unsigned int tls_flags; + char *ocsp_stapling_response; ++ char *ocsp_stapling_response_multi; + char *dh_file; + char *openssl_ciphers; ++ char *openssl_ecdh_curves; + u8 *pac_opaque_encr_key; + u8 *eap_fast_a_id; + size_t eap_fast_a_id_len; +@@ -358,6 +427,7 @@ + + int ap_max_inactivity; + int ignore_broadcast_ssid; ++ int no_probe_resp_if_max_sta; + + int wmm_enabled; + int wmm_uapsd; +@@ -395,9 +465,11 @@ + u8 *extra_cred; + size_t extra_cred_len; + int wps_cred_processing; ++ int wps_cred_add_sae; + int force_per_enrollee_psk; + u8 *ap_settings; + size_t ap_settings_len; ++ struct hostapd_ssid multi_ap_backhaul_ssid; + char *upnp_iface; + char *friendly_name; + char *manufacturer_url; +@@ -440,6 +512,7 @@ + int time_advertisement; + char *time_zone; + int wnm_sleep_mode; ++ int wnm_sleep_mode_no_keys; + int bss_transition; + + /* IEEE 802.11u - Interworking */ +@@ -462,6 +535,10 @@ + unsigned int venue_name_count; + struct hostapd_lang_string *venue_name; + ++ /* Venue URL duples */ ++ unsigned int venue_url_count; ++ struct hostapd_venue_url *venue_url; ++ + /* IEEE 802.11u - Network Authentication Type */ + u8 *network_auth_type; + size_t network_auth_type_len; +@@ -481,8 +558,11 @@ + unsigned int nai_realm_count; + struct hostapd_nai_realm_data *nai_realm_data; + ++ struct dl_list anqp_elem; /* list of struct anqp_element */ ++ + u16 gas_comeback_delay; +- int gas_frag_limit; ++ size_t gas_frag_limit; ++ int gas_address3; + + u8 qos_map_set[16 + 2 * 21]; + unsigned int qos_map_set_len; +@@ -492,6 +572,7 @@ + int na_mcast_to_ucast; + #ifdef CONFIG_HS20 + int hs20; ++ int hs20_release; + int disable_dgaf; + u16 anqp_domain_id; + unsigned int hs20_oper_friendly_name_count; +@@ -520,13 +601,21 @@ + char **icons; + size_t icons_count; + char *osu_nai; ++ char *osu_nai2; + unsigned int service_desc_count; + struct hostapd_lang_string *service_desc; + } *hs20_osu_providers, *last_osu; + size_t hs20_osu_providers_count; ++ size_t hs20_osu_providers_nai_count; ++ char **hs20_operator_icon; ++ size_t hs20_operator_icon_count; + unsigned int hs20_deauth_req_timeout; + char *subscr_remediation_url; + u8 subscr_remediation_method; ++ char *hs20_sim_provisioning_url; ++ char *t_c_filename; ++ u32 t_c_timestamp; ++ char *t_c_server_url; + #endif /* CONFIG_HS20 */ + + u8 wps_rf_bands; /* RF bands for WPS (WPS_RF_*) */ +@@ -536,9 +625,13 @@ + #endif /* CONFIG_RADIUS_TEST */ + + struct wpabuf *vendor_elements; ++ struct wpabuf *assocresp_elements; + + unsigned int sae_anti_clogging_threshold; ++ unsigned int sae_sync; ++ int sae_require_mfp; + int *sae_groups; ++ struct sae_password_entry *sae_passwords; + + char *wowlan_triggers; /* Wake-on-WLAN triggers */ + +@@ -546,21 +639,98 @@ + u8 bss_load_test[5]; + u8 bss_load_test_set; + struct wpabuf *own_ie_override; ++ int sae_reflection_attack; ++ struct wpabuf *sae_commit_override; + #endif /* CONFIG_TESTING_OPTIONS */ + + #define MESH_ENABLED BIT(0) + int mesh; + +- int radio_measurements; ++ u8 radio_measurements[RRM_CAPABILITIES_IE_LEN]; + + int vendor_vht; ++ int use_sta_nsts; + + char *no_probe_resp_if_seen_on; + char *no_auth_if_seen_on; ++ ++ int pbss; ++ ++#ifdef CONFIG_MBO ++ int mbo_enabled; ++ /** ++ * oce - Enable OCE in AP and/or STA-CFON mode ++ * - BIT(0) is Reserved ++ * - Set BIT(1) to enable OCE in STA-CFON mode ++ * - Set BIT(2) to enable OCE in AP mode ++ */ ++ unsigned int oce; ++ int mbo_cell_data_conn_pref; ++#endif /* CONFIG_MBO */ ++ ++ int ftm_responder; ++ int ftm_initiator; ++ ++#ifdef CONFIG_FILS ++ u8 fils_cache_id[FILS_CACHE_ID_LEN]; ++ int fils_cache_id_set; ++ struct dl_list fils_realms; /* list of struct fils_realm */ ++ int fils_dh_group; ++ struct hostapd_ip_addr dhcp_server; ++ int dhcp_rapid_commit_proxy; ++ unsigned int fils_hlp_wait_time; ++ u16 dhcp_server_port; ++ u16 dhcp_relay_port; ++#endif /* CONFIG_FILS */ ++ ++ int multicast_to_unicast; ++ ++ int broadcast_deauth; ++ ++#ifdef CONFIG_DPP ++ char *dpp_connector; ++ struct wpabuf *dpp_netaccesskey; ++ unsigned int dpp_netaccesskey_expiry; ++ struct wpabuf *dpp_csign; ++#endif /* CONFIG_DPP */ ++ ++#ifdef CONFIG_OWE ++ macaddr owe_transition_bssid; ++ u8 owe_transition_ssid[SSID_MAX_LEN]; ++ size_t owe_transition_ssid_len; ++ char owe_transition_ifname[IFNAMSIZ + 1]; ++ int *owe_groups; ++#endif /* CONFIG_OWE */ ++ ++ int coloc_intf_reporting; ++ ++ u8 send_probe_response; ++ ++#define BACKHAUL_BSS 1 ++#define FRONTHAUL_BSS 2 ++ int multi_ap; /* bitmap of BACKHAUL_BSS, FRONTHAUL_BSS */ + }; + ++/** ++ * struct he_phy_capabilities_info - HE PHY capabilities ++ */ ++struct he_phy_capabilities_info { ++ Boolean he_su_beamformer; ++ Boolean he_su_beamformee; ++ Boolean he_mu_beamformer; ++}; + + /** ++ * struct he_operation - HE operation ++ */ ++struct he_operation { ++ u8 he_bss_color; ++ u8 he_default_pe_duration; ++ u8 he_twt_required; ++ u8 he_rts_threshold; ++}; ++ ++/** + * struct hostapd_config - Per-radio interface configuration + */ + struct hostapd_config { +@@ -570,10 +740,10 @@ + u16 beacon_int; + int rts_threshold; + int fragm_threshold; +- u8 send_probe_response; + u8 channel; + u8 acs; + struct wpa_freq_range_list acs_ch_list; ++ int acs_exclude_dfs; + enum hostapd_hw_mode hw_mode; /* HOSTAPD_MODE_IEEE80211A, .. */ + enum { + LONG_PREAMBLE = 0, +@@ -582,6 +752,8 @@ + + int *supported_rates; + int *basic_rates; ++ unsigned int beacon_rate; ++ enum beacon_rate_type rate_type; + + const struct wpa_driver_ops *driver; + char *driver_params; +@@ -597,6 +769,9 @@ + * ' ' (ascii 32): all environments + * 'O': Outdoor environemnt only + * 'I': Indoor environment only ++ * 'X': Used with noncountry entity ("XXX") ++ * 0x00..0x31: identifying IEEE 802.11 standard ++ * Annex E table (0x04 = global table) + */ + + int ieee80211d; +@@ -637,7 +812,11 @@ + u8 vht_oper_chwidth; + u8 vht_oper_centr_freq_seg0_idx; + u8 vht_oper_centr_freq_seg1_idx; ++ u8 ht40_plus_minus_allowed; + ++ /* Use driver-generated interface addresses when adding multiple BSSs */ ++ u8 use_driver_iface_addr; ++ + #ifdef CONFIG_FST + struct fst_iface_cfg fst_cfg; + #endif /* CONFIG_FST */ +@@ -652,6 +831,7 @@ + double ignore_assoc_probability; + double ignore_reassoc_probability; + double corrupt_gtk_rekey_mic_probability; ++ int ecsa_ie_only; + #endif /* CONFIG_TESTING_OPTIONS */ + + #ifdef CONFIG_ACS +@@ -662,25 +842,45 @@ + } *acs_chan_bias; + unsigned int num_acs_chan_bias; + #endif /* CONFIG_ACS */ ++ ++ struct wpabuf *lci; ++ struct wpabuf *civic; ++ int stationary_ap; ++ ++ int ieee80211ax; ++#ifdef CONFIG_IEEE80211AX ++ struct he_phy_capabilities_info he_phy_capab; ++ struct he_operation he_op; ++ struct ieee80211_he_mu_edca_parameter_set he_mu_edca; ++#endif /* CONFIG_IEEE80211AX */ ++ ++ /* VHT enable/disable config from CHAN_SWITCH */ ++#define CH_SWITCH_VHT_ENABLED BIT(0) ++#define CH_SWITCH_VHT_DISABLED BIT(1) ++ unsigned int ch_switch_vht_config; ++ ++ int rssi_reject_assoc_rssi; ++ int rssi_reject_assoc_timeout; + }; + + + int hostapd_mac_comp(const void *a, const void *b); +-int hostapd_mac_comp_empty(const void *a); + struct hostapd_config * hostapd_config_defaults(void); + void hostapd_config_defaults_bss(struct hostapd_bss_config *bss); + void hostapd_config_free_eap_user(struct hostapd_eap_user *user); ++void hostapd_config_free_eap_users(struct hostapd_eap_user *user); + void hostapd_config_clear_wpa_psk(struct hostapd_wpa_psk **p); + void hostapd_config_free_bss(struct hostapd_bss_config *conf); + void hostapd_config_free(struct hostapd_config *conf); + int hostapd_maclist_found(struct mac_acl_entry *list, int num_entries, +- const u8 *addr, int *vlan_id); ++ const u8 *addr, struct vlan_description *vlan_id); + int hostapd_rate_found(int *list, int rate); + const u8 * hostapd_get_psk(const struct hostapd_bss_config *conf, + const u8 *addr, const u8 *p2p_dev_addr, +- const u8 *prev_psk); ++ const u8 *prev_psk, int *vlan_id); + int hostapd_setup_wpa_psk(struct hostapd_bss_config *conf); +-int hostapd_vlan_id_valid(struct hostapd_vlan *vlan, int vlan_id); ++int hostapd_vlan_valid(struct hostapd_vlan *vlan, ++ struct vlan_description *vlan_desc); + const char * hostapd_get_vlan_id_ifname(struct hostapd_vlan *vlan, + int vlan_id); + struct hostapd_radius_attr * +@@ -688,5 +888,6 @@ + int hostapd_config_check(struct hostapd_config *conf, int full_config); + void hostapd_set_security_params(struct hostapd_bss_config *bss, + int full_config); ++int hostapd_sae_pw_id_in_use(struct hostapd_bss_config *conf); + + #endif /* HOSTAPD_CONFIG_H */ +--- contrib/wpa/src/ap/ap_drv_ops.c.orig ++++ contrib/wpa/src/ap/ap_drv_ops.c +@@ -19,6 +19,7 @@ + #include "ap_config.h" + #include "p2p_hostapd.h" + #include "hs20.h" ++#include "wpa_auth.h" + #include "ap_drv_ops.h" + + +@@ -33,10 +34,36 @@ + res |= WPA_STA_SHORT_PREAMBLE; + if (flags & WLAN_STA_MFP) + res |= WPA_STA_MFP; ++ if (flags & WLAN_STA_AUTH) ++ res |= WPA_STA_AUTHENTICATED; ++ if (flags & WLAN_STA_ASSOC) ++ res |= WPA_STA_ASSOCIATED; + return res; + } + + ++static int add_buf(struct wpabuf **dst, const struct wpabuf *src) ++{ ++ if (!src) ++ return 0; ++ if (wpabuf_resize(dst, wpabuf_len(src)) != 0) ++ return -1; ++ wpabuf_put_buf(*dst, src); ++ return 0; ++} ++ ++ ++static int add_buf_data(struct wpabuf **dst, const u8 *data, size_t len) ++{ ++ if (!data || !len) ++ return 0; ++ if (wpabuf_resize(dst, len) != 0) ++ return -1; ++ wpabuf_put_data(*dst, data, len); ++ return 0; ++} ++ ++ + int hostapd_build_ap_extra_ies(struct hostapd_data *hapd, + struct wpabuf **beacon_ret, + struct wpabuf **proberesp_ret, +@@ -49,82 +76,45 @@ + + pos = buf; + pos = hostapd_eid_time_adv(hapd, pos); +- if (pos != buf) { +- if (wpabuf_resize(&beacon, pos - buf) != 0) +- goto fail; +- wpabuf_put_data(beacon, buf, pos - buf); +- } ++ if (add_buf_data(&beacon, buf, pos - buf) < 0) ++ goto fail; + pos = hostapd_eid_time_zone(hapd, pos); +- if (pos != buf) { +- if (wpabuf_resize(&proberesp, pos - buf) != 0) +- goto fail; +- wpabuf_put_data(proberesp, buf, pos - buf); +- } ++ if (add_buf_data(&proberesp, buf, pos - buf) < 0) ++ goto fail; + + pos = buf; + pos = hostapd_eid_ext_capab(hapd, pos); +- if (pos != buf) { +- if (wpabuf_resize(&assocresp, pos - buf) != 0) +- goto fail; +- wpabuf_put_data(assocresp, buf, pos - buf); +- } ++ if (add_buf_data(&assocresp, buf, pos - buf) < 0) ++ goto fail; + pos = hostapd_eid_interworking(hapd, pos); + pos = hostapd_eid_adv_proto(hapd, pos); + pos = hostapd_eid_roaming_consortium(hapd, pos); +- if (pos != buf) { +- if (wpabuf_resize(&beacon, pos - buf) != 0) +- goto fail; +- wpabuf_put_data(beacon, buf, pos - buf); ++ if (add_buf_data(&beacon, buf, pos - buf) < 0 || ++ add_buf_data(&proberesp, buf, pos - buf) < 0) ++ goto fail; + +- if (wpabuf_resize(&proberesp, pos - buf) != 0) +- goto fail; +- wpabuf_put_data(proberesp, buf, pos - buf); +- } +- + #ifdef CONFIG_FST +- if (hapd->iface->fst_ies) { +- size_t add = wpabuf_len(hapd->iface->fst_ies); +- +- if (wpabuf_resize(&beacon, add) < 0) +- goto fail; +- wpabuf_put_buf(beacon, hapd->iface->fst_ies); +- if (wpabuf_resize(&proberesp, add) < 0) +- goto fail; +- wpabuf_put_buf(proberesp, hapd->iface->fst_ies); +- if (wpabuf_resize(&assocresp, add) < 0) +- goto fail; +- wpabuf_put_buf(assocresp, hapd->iface->fst_ies); +- } ++ if (add_buf(&beacon, hapd->iface->fst_ies) < 0 || ++ add_buf(&proberesp, hapd->iface->fst_ies) < 0 || ++ add_buf(&assocresp, hapd->iface->fst_ies) < 0) ++ goto fail; + #endif /* CONFIG_FST */ + +- if (hapd->wps_beacon_ie) { +- if (wpabuf_resize(&beacon, wpabuf_len(hapd->wps_beacon_ie)) < +- 0) +- goto fail; +- wpabuf_put_buf(beacon, hapd->wps_beacon_ie); +- } ++#ifdef CONFIG_FILS ++ pos = hostapd_eid_fils_indic(hapd, buf, 0); ++ if (add_buf_data(&beacon, buf, pos - buf) < 0 || ++ add_buf_data(&proberesp, buf, pos - buf) < 0) ++ goto fail; ++#endif /* CONFIG_FILS */ + +- if (hapd->wps_probe_resp_ie) { +- if (wpabuf_resize(&proberesp, +- wpabuf_len(hapd->wps_probe_resp_ie)) < 0) +- goto fail; +- wpabuf_put_buf(proberesp, hapd->wps_probe_resp_ie); +- } ++ if (add_buf(&beacon, hapd->wps_beacon_ie) < 0 || ++ add_buf(&proberesp, hapd->wps_probe_resp_ie) < 0) ++ goto fail; + + #ifdef CONFIG_P2P +- if (hapd->p2p_beacon_ie) { +- if (wpabuf_resize(&beacon, wpabuf_len(hapd->p2p_beacon_ie)) < +- 0) +- goto fail; +- wpabuf_put_buf(beacon, hapd->p2p_beacon_ie); +- } +- +- if (hapd->p2p_probe_resp_ie) { +- if (wpabuf_resize(&proberesp, +- wpabuf_len(hapd->p2p_probe_resp_ie)) < 0) +- goto fail; +- wpabuf_put_buf(proberesp, hapd->p2p_probe_resp_ie); +- } ++ if (add_buf(&beacon, hapd->p2p_beacon_ie) < 0 || ++ add_buf(&proberesp, hapd->p2p_probe_resp_ie) < 0) ++ goto fail; + #endif /* CONFIG_P2P */ + + #ifdef CONFIG_P2P_MANAGER +@@ -148,8 +138,7 @@ + #ifdef CONFIG_WPS + if (hapd->conf->wps_state) { + struct wpabuf *a = wps_build_assoc_resp_ie(); +- if (a && wpabuf_resize(&assocresp, wpabuf_len(a)) == 0) +- wpabuf_put_buf(assocresp, a); ++ add_buf(&assocresp, a); + wpabuf_free(a); + } + #endif /* CONFIG_WPS */ +@@ -169,45 +158,45 @@ + if (hapd->p2p_group) { + struct wpabuf *a; + a = p2p_group_assoc_resp_ie(hapd->p2p_group, P2P_SC_SUCCESS); +- if (a && wpabuf_resize(&assocresp, wpabuf_len(a)) == 0) +- wpabuf_put_buf(assocresp, a); ++ add_buf(&assocresp, a); + wpabuf_free(a); + } + #endif /* CONFIG_WIFI_DISPLAY */ + + #ifdef CONFIG_HS20 +- pos = buf; +- pos = hostapd_eid_hs20_indication(hapd, pos); +- if (pos != buf) { +- if (wpabuf_resize(&beacon, pos - buf) != 0) +- goto fail; +- wpabuf_put_data(beacon, buf, pos - buf); ++ pos = hostapd_eid_hs20_indication(hapd, buf); ++ if (add_buf_data(&beacon, buf, pos - buf) < 0 || ++ add_buf_data(&proberesp, buf, pos - buf) < 0) ++ goto fail; + +- if (wpabuf_resize(&proberesp, pos - buf) != 0) +- goto fail; +- wpabuf_put_data(proberesp, buf, pos - buf); +- } +- + pos = hostapd_eid_osen(hapd, buf); +- if (pos != buf) { +- if (wpabuf_resize(&beacon, pos - buf) != 0) +- goto fail; +- wpabuf_put_data(beacon, buf, pos - buf); ++ if (add_buf_data(&beacon, buf, pos - buf) < 0 || ++ add_buf_data(&proberesp, buf, pos - buf) < 0) ++ goto fail; ++#endif /* CONFIG_HS20 */ + +- if (wpabuf_resize(&proberesp, pos - buf) != 0) ++#ifdef CONFIG_MBO ++ if (hapd->conf->mbo_enabled || ++ OCE_STA_CFON_ENABLED(hapd) || OCE_AP_ENABLED(hapd)) { ++ pos = hostapd_eid_mbo(hapd, buf, sizeof(buf)); ++ if (add_buf_data(&beacon, buf, pos - buf) < 0 || ++ add_buf_data(&proberesp, buf, pos - buf) < 0 || ++ add_buf_data(&assocresp, buf, pos - buf) < 0) + goto fail; +- wpabuf_put_data(proberesp, buf, pos - buf); + } +-#endif /* CONFIG_HS20 */ ++#endif /* CONFIG_MBO */ + +- if (hapd->conf->vendor_elements) { +- size_t add = wpabuf_len(hapd->conf->vendor_elements); +- if (wpabuf_resize(&beacon, add) == 0) +- wpabuf_put_buf(beacon, hapd->conf->vendor_elements); +- if (wpabuf_resize(&proberesp, add) == 0) +- wpabuf_put_buf(proberesp, hapd->conf->vendor_elements); +- } ++#ifdef CONFIG_OWE ++ pos = hostapd_eid_owe_trans(hapd, buf, sizeof(buf)); ++ if (add_buf_data(&beacon, buf, pos - buf) < 0 || ++ add_buf_data(&proberesp, buf, pos - buf) < 0) ++ goto fail; ++#endif /* CONFIG_OWE */ + ++ add_buf(&beacon, hapd->conf->vendor_elements); ++ add_buf(&proberesp, hapd->conf->vendor_elements); ++ add_buf(&assocresp, hapd->conf->assocresp_elements); ++ + *beacon_ret = beacon; + *proberesp_ret = proberesp; + *assocresp_ret = assocresp; +@@ -367,10 +356,44 @@ + int hostapd_sta_auth(struct hostapd_data *hapd, const u8 *addr, + u16 seq, u16 status, const u8 *ie, size_t len) + { ++ struct wpa_driver_sta_auth_params params; ++#ifdef CONFIG_FILS ++ struct sta_info *sta; ++#endif /* CONFIG_FILS */ ++ + if (hapd->driver == NULL || hapd->driver->sta_auth == NULL) + return 0; +- return hapd->driver->sta_auth(hapd->drv_priv, hapd->own_addr, addr, +- seq, status, ie, len); ++ ++ os_memset(¶ms, 0, sizeof(params)); ++ ++#ifdef CONFIG_FILS ++ sta = ap_get_sta(hapd, addr); ++ if (!sta) { ++ wpa_printf(MSG_DEBUG, "Station " MACSTR ++ " not found for sta_auth processing", ++ MAC2STR(addr)); ++ return 0; ++ } ++ ++ if (sta->auth_alg == WLAN_AUTH_FILS_SK || ++ sta->auth_alg == WLAN_AUTH_FILS_SK_PFS || ++ sta->auth_alg == WLAN_AUTH_FILS_PK) { ++ params.fils_auth = 1; ++ wpa_auth_get_fils_aead_params(sta->wpa_sm, params.fils_anonce, ++ params.fils_snonce, ++ params.fils_kek, ++ ¶ms.fils_kek_len); ++ } ++#endif /* CONFIG_FILS */ ++ ++ params.own_addr = hapd->own_addr; ++ params.addr = addr; ++ params.seq = seq; ++ params.status = status; ++ params.ie = ie; ++ params.len = len; ++ ++ return hapd->driver->sta_auth(hapd->drv_priv, ¶ms); + } + + +@@ -390,7 +413,8 @@ + u16 listen_interval, + const struct ieee80211_ht_capabilities *ht_capab, + const struct ieee80211_vht_capabilities *vht_capab, +- u32 flags, u8 qosinfo, u8 vht_opmode) ++ u32 flags, u8 qosinfo, u8 vht_opmode, int supp_p2p_ps, ++ int set) + { + struct hostapd_sta_add_params params; + +@@ -412,6 +436,8 @@ + params.vht_opmode = vht_opmode; + params.flags = hostapd_sta_flags_to_drv(flags); + params.qosinfo = qosinfo; ++ params.support_p2p_ps = supp_p2p_ps; ++ params.set = set; + return hapd->driver->sta_add(hapd->drv_priv, ¶ms); + } + +@@ -468,7 +494,7 @@ + return -1; + return hapd->driver->if_add(hapd->drv_priv, type, ifname, addr, + bss_ctx, drv_priv, force_ifname, if_addr, +- bridge, use_existing); ++ bridge, use_existing, 1); + } + + +@@ -578,13 +604,13 @@ + + struct hostapd_hw_modes * + hostapd_get_hw_feature_data(struct hostapd_data *hapd, u16 *num_modes, +- u16 *flags) ++ u16 *flags, u8 *dfs_domain) + { + if (hapd->driver == NULL || + hapd->driver->get_hw_feature_data == NULL) + return NULL; + return hapd->driver->get_hw_feature_data(hapd->drv_priv, num_modes, +- flags); ++ flags, dfs_domain); + } + + +@@ -647,9 +673,21 @@ + int hostapd_drv_send_mlme(struct hostapd_data *hapd, + const void *msg, size_t len, int noack) + { ++ if (!hapd->driver || !hapd->driver->send_mlme || !hapd->drv_priv) ++ return 0; ++ return hapd->driver->send_mlme(hapd->drv_priv, msg, len, noack, 0, ++ NULL, 0); ++} ++ ++ ++int hostapd_drv_send_mlme_csa(struct hostapd_data *hapd, ++ const void *msg, size_t len, int noack, ++ const u16 *csa_offs, size_t csa_offs_len) ++{ + if (hapd->driver == NULL || hapd->driver->send_mlme == NULL) + return 0; +- return hapd->driver->send_mlme(hapd->drv_priv, msg, len, noack, 0); ++ return hapd->driver->send_mlme(hapd->drv_priv, msg, len, noack, 0, ++ csa_offs, csa_offs_len); + } + + +@@ -656,7 +694,7 @@ + int hostapd_drv_sta_deauth(struct hostapd_data *hapd, + const u8 *addr, int reason) + { +- if (hapd->driver == NULL || hapd->driver->sta_deauth == NULL) ++ if (!hapd->driver || !hapd->driver->sta_deauth || !hapd->drv_priv) + return 0; + return hapd->driver->sta_deauth(hapd->drv_priv, hapd->own_addr, addr, + reason); +@@ -666,7 +704,7 @@ + int hostapd_drv_sta_disassoc(struct hostapd_data *hapd, + const u8 *addr, int reason) + { +- if (hapd->driver == NULL || hapd->driver->sta_disassoc == NULL) ++ if (!hapd->driver || !hapd->driver->sta_disassoc || !hapd->drv_priv) + return 0; + return hapd->driver->sta_disassoc(hapd->drv_priv, hapd->own_addr, addr, + reason); +@@ -687,6 +725,45 @@ + unsigned int wait, const u8 *dst, const u8 *data, + size_t len) + { ++ const u8 *bssid; ++ const u8 wildcard_bssid[ETH_ALEN] = { ++ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff ++ }; ++ ++ if (!hapd->driver || !hapd->driver->send_action || !hapd->drv_priv) ++ return 0; ++ bssid = hapd->own_addr; ++ if (!is_multicast_ether_addr(dst) && ++ len > 0 && data[0] == WLAN_ACTION_PUBLIC) { ++ struct sta_info *sta; ++ ++ /* ++ * Public Action frames to a STA that is not a member of the BSS ++ * shall use wildcard BSSID value. ++ */ ++ sta = ap_get_sta(hapd, dst); ++ if (!sta || !(sta->flags & WLAN_STA_ASSOC)) ++ bssid = wildcard_bssid; ++ } else if (is_broadcast_ether_addr(dst) && ++ len > 0 && data[0] == WLAN_ACTION_PUBLIC) { ++ /* ++ * The only current use case of Public Action frames with ++ * broadcast destination address is DPP PKEX. That case is ++ * directing all devices and not just the STAs within the BSS, ++ * so have to use the wildcard BSSID value. ++ */ ++ bssid = wildcard_bssid; ++ } ++ return hapd->driver->send_action(hapd->drv_priv, freq, wait, dst, ++ hapd->own_addr, bssid, data, len, 0); ++} ++ ++ ++int hostapd_drv_send_action_addr3_ap(struct hostapd_data *hapd, ++ unsigned int freq, ++ unsigned int wait, const u8 *dst, ++ const u8 *data, size_t len) ++{ + if (hapd->driver == NULL || hapd->driver->send_action == NULL) + return 0; + return hapd->driver->send_action(hapd->drv_priv, freq, wait, dst, +@@ -736,7 +813,7 @@ + int hostapd_drv_set_qos_map(struct hostapd_data *hapd, + const u8 *qos_map_set, u8 qos_map_set_len) + { +- if (hapd->driver == NULL || hapd->driver->set_qos_map == NULL) ++ if (!hapd->driver || !hapd->driver->set_qos_map || !hapd->drv_priv) + return 0; + return hapd->driver->set_qos_map(hapd->drv_priv, qos_map_set, + qos_map_set_len); +@@ -756,12 +833,28 @@ + if ((acs_ch_list_all || + freq_range_list_includes(&hapd->iface->conf->acs_ch_list, + chan->chan)) && +- !(chan->flag & HOSTAPD_CHAN_DISABLED)) ++ !(chan->flag & HOSTAPD_CHAN_DISABLED) && ++ !(hapd->iface->conf->acs_exclude_dfs && ++ (chan->flag & HOSTAPD_CHAN_RADAR))) + int_array_add_unique(freq_list, chan->freq); + } + } + + ++void hostapd_get_ext_capa(struct hostapd_iface *iface) ++{ ++ struct hostapd_data *hapd = iface->bss[0]; ++ ++ if (!hapd->driver || !hapd->driver->get_ext_capab) ++ return; ++ ++ hapd->driver->get_ext_capab(hapd->drv_priv, WPA_IF_AP_BSS, ++ &iface->extended_capa, ++ &iface->extended_capa_mask, ++ &iface->extended_capa_len); ++} ++ ++ + int hostapd_drv_do_acs(struct hostapd_data *hapd) + { + struct drv_acs_params params; +@@ -797,6 +890,9 @@ + &hapd->iface->conf->acs_ch_list, + chan->chan)) + continue; ++ if (hapd->iface->conf->acs_exclude_dfs && ++ (chan->flag & HOSTAPD_CHAN_RADAR)) ++ continue; + if (!(chan->flag & HOSTAPD_CHAN_DISABLED)) { + channels[num_channels++] = chan->chan; + int_array_add_unique(&freq_list, chan->freq); +--- contrib/wpa/src/ap/ap_drv_ops.h.orig ++++ contrib/wpa/src/ap/ap_drv_ops.h +@@ -41,7 +41,8 @@ + u16 listen_interval, + const struct ieee80211_ht_capabilities *ht_capab, + const struct ieee80211_vht_capabilities *vht_capab, +- u32 flags, u8 qosinfo, u8 vht_opmode); ++ u32 flags, u8 qosinfo, u8 vht_opmode, int supp_p2p_ps, ++ int set); + int hostapd_set_privacy(struct hostapd_data *hapd, int enabled); + int hostapd_set_generic_elem(struct hostapd_data *hapd, const u8 *elem, + size_t elem_len); +@@ -71,7 +72,7 @@ + int cw_min, int cw_max, int burst_time); + struct hostapd_hw_modes * + hostapd_get_hw_feature_data(struct hostapd_data *hapd, u16 *num_modes, +- u16 *flags); ++ u16 *flags, u8 *dfs_domain); + int hostapd_driver_commit(struct hostapd_data *hapd); + int hostapd_drv_none(struct hostapd_data *hapd); + int hostapd_driver_scan(struct hostapd_data *hapd, +@@ -88,6 +89,9 @@ + const u8 *key, size_t key_len); + int hostapd_drv_send_mlme(struct hostapd_data *hapd, + const void *msg, size_t len, int noack); ++int hostapd_drv_send_mlme_csa(struct hostapd_data *hapd, ++ const void *msg, size_t len, int noack, ++ const u16 *csa_offs, size_t csa_offs_len); + int hostapd_drv_sta_deauth(struct hostapd_data *hapd, + const u8 *addr, int reason); + int hostapd_drv_sta_disassoc(struct hostapd_data *hapd, +@@ -95,6 +99,18 @@ + int hostapd_drv_send_action(struct hostapd_data *hapd, unsigned int freq, + unsigned int wait, const u8 *dst, const u8 *data, + size_t len); ++int hostapd_drv_send_action_addr3_ap(struct hostapd_data *hapd, ++ unsigned int freq, ++ unsigned int wait, const u8 *dst, ++ const u8 *data, size_t len); ++static inline void ++hostapd_drv_send_action_cancel_wait(struct hostapd_data *hapd) ++{ ++ if (!hapd->driver || !hapd->driver->send_action_cancel_wait || ++ !hapd->drv_priv) ++ return; ++ hapd->driver->send_action_cancel_wait(hapd->drv_priv); ++} + int hostapd_add_sta_node(struct hostapd_data *hapd, const u8 *addr, + u16 auth_alg); + int hostapd_sta_auth(struct hostapd_data *hapd, const u8 *addr, +@@ -120,6 +136,8 @@ + int hostapd_drv_set_qos_map(struct hostapd_data *hapd, const u8 *qos_map_set, + u8 qos_map_set_len); + ++void hostapd_get_ext_capa(struct hostapd_iface *iface); ++ + static inline int hostapd_drv_set_countermeasures(struct hostapd_data *hapd, + int enabled) + { +@@ -150,7 +168,7 @@ + static inline int hostapd_drv_sta_remove(struct hostapd_data *hapd, + const u8 *addr) + { +- if (hapd->driver == NULL || hapd->driver->sta_remove == NULL) ++ if (!hapd->driver || !hapd->driver->sta_remove || !hapd->drv_priv) + return 0; + return hapd->driver->sta_remove(hapd->drv_priv, addr); + } +@@ -264,8 +282,9 @@ + static inline int hostapd_drv_switch_channel(struct hostapd_data *hapd, + struct csa_settings *settings) + { +- if (hapd->driver == NULL || hapd->driver->switch_channel == NULL) +- return -ENOTSUP; ++ if (hapd->driver == NULL || hapd->driver->switch_channel == NULL || ++ hapd->drv_priv == NULL) ++ return -1; + + return hapd->driver->switch_channel(hapd->drv_priv, settings); + } +@@ -273,7 +292,7 @@ + static inline int hostapd_drv_status(struct hostapd_data *hapd, char *buf, + size_t buflen) + { +- if (hapd->driver == NULL || hapd->driver->status == NULL) ++ if (!hapd->driver || !hapd->driver->status || !hapd->drv_priv) + return -1; + return hapd->driver->status(hapd->drv_priv, buf, buflen); + } +@@ -332,9 +351,27 @@ + + static inline int hostapd_drv_stop_ap(struct hostapd_data *hapd) + { +- if (hapd->driver == NULL || hapd->driver->stop_ap == NULL) ++ if (!hapd->driver || !hapd->driver->stop_ap || !hapd->drv_priv) + return 0; + return hapd->driver->stop_ap(hapd->drv_priv); + } + ++static inline int hostapd_drv_channel_info(struct hostapd_data *hapd, ++ struct wpa_channel_info *ci) ++{ ++ if (!hapd->driver || !hapd->driver->channel_info) ++ return -1; ++ return hapd->driver->channel_info(hapd->drv_priv, ci); ++} ++ ++static inline int ++hostapd_drv_send_external_auth_status(struct hostapd_data *hapd, ++ struct external_auth *params) ++{ ++ if (!hapd->driver || !hapd->drv_priv || ++ !hapd->driver->send_external_auth_status) ++ return -1; ++ return hapd->driver->send_external_auth_status(hapd->drv_priv, params); ++} ++ + #endif /* AP_DRV_OPS */ +--- contrib/wpa/src/ap/ap_mlme.c.orig ++++ contrib/wpa/src/ap/ap_mlme.c +@@ -57,8 +57,13 @@ + HOSTAPD_LEVEL_DEBUG, + "MLME-AUTHENTICATE.indication(" MACSTR ", %s)", + MAC2STR(sta->addr), mlme_auth_alg_str(sta->auth_alg)); +- if (sta->auth_alg != WLAN_AUTH_FT && !(sta->flags & WLAN_STA_MFP)) ++ if (sta->auth_alg != WLAN_AUTH_FT && ++ sta->auth_alg != WLAN_AUTH_FILS_SK && ++ sta->auth_alg != WLAN_AUTH_FILS_SK_PFS && ++ sta->auth_alg != WLAN_AUTH_FILS_PK && ++ !(sta->flags & WLAN_STA_MFP)) + mlme_deletekeys_request(hapd, sta); ++ ap_sta_clear_disconnect_timeouts(hapd, sta); + } + + +@@ -104,8 +109,12 @@ + HOSTAPD_LEVEL_DEBUG, + "MLME-ASSOCIATE.indication(" MACSTR ")", + MAC2STR(sta->addr)); +- if (sta->auth_alg != WLAN_AUTH_FT) ++ if (sta->auth_alg != WLAN_AUTH_FT && ++ sta->auth_alg != WLAN_AUTH_FILS_SK && ++ sta->auth_alg != WLAN_AUTH_FILS_SK_PFS && ++ sta->auth_alg != WLAN_AUTH_FILS_PK) + mlme_deletekeys_request(hapd, sta); ++ ap_sta_clear_disconnect_timeouts(hapd, sta); + } + + +@@ -128,8 +137,12 @@ + HOSTAPD_LEVEL_DEBUG, + "MLME-REASSOCIATE.indication(" MACSTR ")", + MAC2STR(sta->addr)); +- if (sta->auth_alg != WLAN_AUTH_FT) ++ if (sta->auth_alg != WLAN_AUTH_FT && ++ sta->auth_alg != WLAN_AUTH_FILS_SK && ++ sta->auth_alg != WLAN_AUTH_FILS_SK_PFS && ++ sta->auth_alg != WLAN_AUTH_FILS_PK) + mlme_deletekeys_request(hapd, sta); ++ ap_sta_clear_disconnect_timeouts(hapd, sta); + } + + +--- contrib/wpa/src/ap/authsrv.c.orig ++++ contrib/wpa/src/ap/authsrv.c +@@ -71,13 +71,19 @@ + } + + if (eap_user->password) { +- user->password = os_malloc(eap_user->password_len); ++ user->password = os_memdup(eap_user->password, ++ eap_user->password_len); + if (user->password == NULL) + goto out; +- os_memcpy(user->password, eap_user->password, +- eap_user->password_len); + user->password_len = eap_user->password_len; + user->password_hash = eap_user->password_hash; ++ if (eap_user->salt && eap_user->salt_len) { ++ user->salt = os_memdup(eap_user->salt, ++ eap_user->salt_len); ++ if (!user->salt) ++ goto out; ++ user->salt_len = eap_user->salt_len; ++ } + } + user->force_version = eap_user->force_version; + user->macacl = eap_user->macacl; +@@ -84,6 +90,7 @@ + user->ttls_auth = eap_user->ttls_auth; + user->remediation = eap_user->remediation; + user->accept_attr = eap_user->accept_attr; ++ user->t_c_timestamp = eap_user->t_c_timestamp; + rv = 0; + + out: +@@ -129,10 +136,13 @@ + #ifdef CONFIG_HS20 + srv.subscr_remediation_url = conf->subscr_remediation_url; + srv.subscr_remediation_method = conf->subscr_remediation_method; ++ srv.hs20_sim_provisioning_url = conf->hs20_sim_provisioning_url; ++ srv.t_c_server_url = conf->t_c_server_url; + #endif /* CONFIG_HS20 */ + srv.erp = conf->eap_server_erp; + srv.erp_domain = conf->erp_domain; + srv.tls_session_lifetime = conf->tls_session_lifetime; ++ srv.tls_flags = conf->tls_flags; + + hapd->radius_srv = radius_server_init(&srv); + if (hapd->radius_srv == NULL) { +@@ -146,6 +156,40 @@ + #endif /* RADIUS_SERVER */ + + ++#ifdef EAP_TLS_FUNCS ++static void authsrv_tls_event(void *ctx, enum tls_event ev, ++ union tls_event_data *data) ++{ ++ switch (ev) { ++ case TLS_CERT_CHAIN_SUCCESS: ++ wpa_printf(MSG_DEBUG, "authsrv: remote certificate verification success"); ++ break; ++ case TLS_CERT_CHAIN_FAILURE: ++ wpa_printf(MSG_INFO, "authsrv: certificate chain failure: reason=%d depth=%d subject='%s' err='%s'", ++ data->cert_fail.reason, ++ data->cert_fail.depth, ++ data->cert_fail.subject, ++ data->cert_fail.reason_txt); ++ break; ++ case TLS_PEER_CERTIFICATE: ++ wpa_printf(MSG_DEBUG, "authsrv: peer certificate: depth=%d serial_num=%s subject=%s", ++ data->peer_cert.depth, ++ data->peer_cert.serial_num ? data->peer_cert.serial_num : "N/A", ++ data->peer_cert.subject); ++ break; ++ case TLS_ALERT: ++ if (data->alert.is_local) ++ wpa_printf(MSG_DEBUG, "authsrv: local TLS alert: %s", ++ data->alert.description); ++ else ++ wpa_printf(MSG_DEBUG, "authsrv: remote TLS alert: %s", ++ data->alert.description); ++ break; ++ } ++} ++#endif /* EAP_TLS_FUNCS */ ++ ++ + int authsrv_init(struct hostapd_data *hapd) + { + #ifdef EAP_TLS_FUNCS +@@ -157,6 +201,19 @@ + + os_memset(&conf, 0, sizeof(conf)); + conf.tls_session_lifetime = hapd->conf->tls_session_lifetime; ++ if (hapd->conf->crl_reload_interval > 0 && ++ hapd->conf->check_crl <= 0) { ++ wpa_printf(MSG_INFO, ++ "Cannot enable CRL reload functionality - it depends on check_crl being set"); ++ } else if (hapd->conf->crl_reload_interval > 0) { ++ conf.crl_reload_interval = ++ hapd->conf->crl_reload_interval; ++ wpa_printf(MSG_INFO, ++ "Enabled CRL reload functionality"); ++ } ++ conf.tls_flags = hapd->conf->tls_flags; ++ conf.event_cb = authsrv_tls_event; ++ conf.cb_ctx = hapd; + hapd->ssl_ctx = tls_init(&conf); + if (hapd->ssl_ctx == NULL) { + wpa_printf(MSG_ERROR, "Failed to initialize TLS"); +@@ -171,8 +228,12 @@ + params.private_key_passwd = hapd->conf->private_key_passwd; + params.dh_file = hapd->conf->dh_file; + params.openssl_ciphers = hapd->conf->openssl_ciphers; ++ params.openssl_ecdh_curves = hapd->conf->openssl_ecdh_curves; + params.ocsp_stapling_response = + hapd->conf->ocsp_stapling_response; ++ params.ocsp_stapling_response_multi = ++ hapd->conf->ocsp_stapling_response_multi; ++ params.check_cert_subject = hapd->conf->check_cert_subject; + + if (tls_global_set_params(hapd->ssl_ctx, ¶ms)) { + wpa_printf(MSG_ERROR, "Failed to set TLS parameters"); +@@ -181,7 +242,8 @@ + } + + if (tls_global_set_verify(hapd->ssl_ctx, +- hapd->conf->check_crl)) { ++ hapd->conf->check_crl, ++ hapd->conf->check_crl_strict)) { + wpa_printf(MSG_ERROR, "Failed to enable check_crl"); + authsrv_deinit(hapd); + return -1; +@@ -193,6 +255,7 @@ + if (hapd->conf->eap_sim_db) { + hapd->eap_sim_db_priv = + eap_sim_db_init(hapd->conf->eap_sim_db, ++ hapd->conf->eap_sim_db_timeout, + hostapd_sim_db_cb, hapd); + if (hapd->eap_sim_db_priv == NULL) { + wpa_printf(MSG_ERROR, "Failed to initialize EAP-SIM " +--- contrib/wpa/src/ap/beacon.c.orig ++++ contrib/wpa/src/ap/beacon.c +@@ -16,6 +16,7 @@ + #include "common/ieee802_11_defs.h" + #include "common/ieee802_11_common.h" + #include "common/hw_features_common.h" ++#include "common/wpa_ctrl.h" + #include "wps/wps_defs.h" + #include "p2p/p2p.h" + #include "hostapd.h" +@@ -29,6 +30,8 @@ + #include "beacon.h" + #include "hs20.h" + #include "dfs.h" ++#include "taxonomy.h" ++#include "ieee802_11_auth.h" + + + #ifdef NEED_AP_MLME +@@ -36,18 +39,21 @@ + static u8 * hostapd_eid_rm_enabled_capab(struct hostapd_data *hapd, u8 *eid, + size_t len) + { +- if (!hapd->conf->radio_measurements || len < 2 + 4) ++ size_t i; ++ ++ for (i = 0; i < RRM_CAPABILITIES_IE_LEN; i++) { ++ if (hapd->conf->radio_measurements[i]) ++ break; ++ } ++ ++ if (i == RRM_CAPABILITIES_IE_LEN || len < 2 + RRM_CAPABILITIES_IE_LEN) + return eid; + + *eid++ = WLAN_EID_RRM_ENABLED_CAPABILITIES; +- *eid++ = 5; +- *eid++ = (hapd->conf->radio_measurements & BIT(0)) ? +- WLAN_RRM_CAPS_NEIGHBOR_REPORT : 0x00; +- *eid++ = 0x00; +- *eid++ = 0x00; +- *eid++ = 0x00; +- *eid++ = 0x00; +- return eid; ++ *eid++ = RRM_CAPABILITIES_IE_LEN; ++ os_memcpy(eid, hapd->conf->radio_measurements, RRM_CAPABILITIES_IE_LEN); ++ ++ return eid + RRM_CAPABILITIES_IE_LEN; + } + + +@@ -297,19 +303,18 @@ + + static u8 * hostapd_eid_csa(struct hostapd_data *hapd, u8 *eid) + { +- u8 chan; +- +- if (!hapd->cs_freq_params.freq) ++#ifdef CONFIG_TESTING_OPTIONS ++ if (hapd->iface->cs_oper_class && hapd->iconf->ecsa_ie_only) + return eid; ++#endif /* CONFIG_TESTING_OPTIONS */ + +- if (ieee80211_freq_to_chan(hapd->cs_freq_params.freq, &chan) == +- NUM_HOSTAPD_MODES) ++ if (!hapd->cs_freq_params.channel) + return eid; + + *eid++ = WLAN_EID_CHANNEL_SWITCH; + *eid++ = 3; + *eid++ = hapd->cs_block_tx; +- *eid++ = chan; ++ *eid++ = hapd->cs_freq_params.channel; + *eid++ = hapd->cs_count; + + return eid; +@@ -316,46 +321,47 @@ + } + + +-static u8 * hostapd_eid_secondary_channel(struct hostapd_data *hapd, u8 *eid) ++static u8 * hostapd_eid_ecsa(struct hostapd_data *hapd, u8 *eid) + { +- u8 sec_ch; +- +- if (!hapd->cs_freq_params.sec_channel_offset) ++ if (!hapd->cs_freq_params.channel || !hapd->iface->cs_oper_class) + return eid; + +- if (hapd->cs_freq_params.sec_channel_offset == -1) +- sec_ch = HT_INFO_HT_PARAM_SECONDARY_CHNL_BELOW; +- else if (hapd->cs_freq_params.sec_channel_offset == 1) +- sec_ch = HT_INFO_HT_PARAM_SECONDARY_CHNL_ABOVE; +- else +- return eid; ++ *eid++ = WLAN_EID_EXT_CHANSWITCH_ANN; ++ *eid++ = 4; ++ *eid++ = hapd->cs_block_tx; ++ *eid++ = hapd->iface->cs_oper_class; ++ *eid++ = hapd->cs_freq_params.channel; ++ *eid++ = hapd->cs_count; + +- *eid++ = WLAN_EID_SECONDARY_CHANNEL_OFFSET; +- *eid++ = 1; +- *eid++ = sec_ch; +- + return eid; + } + + +-static u8 * hostapd_add_csa_elems(struct hostapd_data *hapd, u8 *pos, +- u8 *start, unsigned int *csa_counter_off) ++static u8 * hostapd_eid_supported_op_classes(struct hostapd_data *hapd, u8 *eid) + { +- u8 *old_pos = pos; ++ u8 op_class, channel; + +- if (!csa_counter_off) +- return pos; ++ if (!(hapd->iface->drv_flags & WPA_DRIVER_FLAGS_AP_CSA) || ++ !hapd->iface->freq) ++ return eid; + +- *csa_counter_off = 0; +- pos = hostapd_eid_csa(hapd, pos); ++ if (ieee80211_freq_to_channel_ext(hapd->iface->freq, ++ hapd->iconf->secondary_channel, ++ hapd->iconf->vht_oper_chwidth, ++ &op_class, &channel) == ++ NUM_HOSTAPD_MODES) ++ return eid; + +- if (pos != old_pos) { +- /* save an offset to the counter - should be last byte */ +- *csa_counter_off = pos - start - 1; +- pos = hostapd_eid_secondary_channel(hapd, pos); +- } ++ *eid++ = WLAN_EID_SUPPORTED_OPERATING_CLASSES; ++ *eid++ = 2; + +- return pos; ++ /* Current Operating Class */ ++ *eid++ = op_class; ++ ++ /* TODO: Advertise all the supported operating classes */ ++ *eid++ = 0; ++ ++ return eid; + } + + +@@ -364,7 +370,7 @@ + int is_p2p, size_t *resp_len) + { + struct ieee80211_mgmt *resp; +- u8 *pos, *epos; ++ u8 *pos, *epos, *csa_pos; + size_t buflen; + + #define MAX_PROBERESP_LEN 768 +@@ -387,6 +393,18 @@ + buflen += 5 + 2 + sizeof(struct ieee80211_vht_capabilities) + + 2 + sizeof(struct ieee80211_vht_operation); + } ++ ++#ifdef CONFIG_IEEE80211AX ++ if (hapd->iconf->ieee80211ax) { ++ buflen += 3 + sizeof(struct ieee80211_he_capabilities) + ++ 3 + sizeof(struct ieee80211_he_operation) + ++ 3 + sizeof(struct ieee80211_he_mu_edca_parameter_set); ++ } ++#endif /* CONFIG_IEEE80211AX */ ++ ++ buflen += hostapd_mbo_ie_len(hapd); ++ buflen += hostapd_eid_owe_trans_len(hapd); ++ + resp = os_zalloc(buflen); + if (resp == NULL) + return NULL; +@@ -424,6 +442,12 @@ + /* Power Constraint element */ + pos = hostapd_eid_pwr_constraint(hapd, pos); + ++ /* CSA IE */ ++ csa_pos = hostapd_eid_csa(hapd, pos); ++ if (csa_pos != pos) ++ hapd->cs_c_off_proberesp = csa_pos - (u8 *) resp - 1; ++ pos = csa_pos; ++ + /* ERP Information element */ + pos = hostapd_eid_erp_info(hapd, pos); + +@@ -430,14 +454,27 @@ + /* Extended supported rates */ + pos = hostapd_eid_ext_supp_rates(hapd, pos); + +- /* RSN, MDIE, WPA */ +- pos = hostapd_eid_wpa(hapd, pos, epos - pos); ++ /* RSN, MDIE */ ++ if (hapd->conf->wpa != WPA_PROTO_WPA) ++ pos = hostapd_eid_wpa(hapd, pos, epos - pos); + + pos = hostapd_eid_bss_load(hapd, pos, epos - pos); + + pos = hostapd_eid_rm_enabled_capab(hapd, pos, epos - pos); + ++ /* eCSA IE */ ++ csa_pos = hostapd_eid_ecsa(hapd, pos); ++ if (csa_pos != pos) ++ hapd->cs_c_off_ecsa_proberesp = csa_pos - (u8 *) resp - 1; ++ pos = csa_pos; ++ ++ pos = hostapd_eid_supported_op_classes(hapd, pos); ++ + #ifdef CONFIG_IEEE80211N ++ /* Secondary Channel Offset element */ ++ /* TODO: The standard doesn't specify a position for this element. */ ++ pos = hostapd_eid_secondary_channel(hapd, pos); ++ + pos = hostapd_eid_ht_capabilities(hapd, pos); + pos = hostapd_eid_ht_operation(hapd, pos); + #endif /* CONFIG_IEEE80211N */ +@@ -451,9 +488,6 @@ + pos = hostapd_eid_adv_proto(hapd, pos); + pos = hostapd_eid_roaming_consortium(hapd, pos); + +- pos = hostapd_add_csa_elems(hapd, pos, (u8 *)resp, +- &hapd->cs_c_off_proberesp); +- + #ifdef CONFIG_FST + if (hapd->iface->fst_ies) { + os_memcpy(pos, wpabuf_head(hapd->iface->fst_ies), +@@ -464,13 +498,32 @@ + + #ifdef CONFIG_IEEE80211AC + if (hapd->iconf->ieee80211ac && !hapd->conf->disable_11ac) { +- pos = hostapd_eid_vht_capabilities(hapd, pos); ++ pos = hostapd_eid_vht_capabilities(hapd, pos, 0); + pos = hostapd_eid_vht_operation(hapd, pos); ++ pos = hostapd_eid_txpower_envelope(hapd, pos); ++ pos = hostapd_eid_wb_chsw_wrapper(hapd, pos); + } ++#endif /* CONFIG_IEEE80211AC */ ++ ++ pos = hostapd_eid_fils_indic(hapd, pos, 0); ++ ++#ifdef CONFIG_IEEE80211AX ++ if (hapd->iconf->ieee80211ax) { ++ pos = hostapd_eid_he_capab(hapd, pos); ++ pos = hostapd_eid_he_operation(hapd, pos); ++ pos = hostapd_eid_he_mu_edca_parameter_set(hapd, pos); ++ } ++#endif /* CONFIG_IEEE80211AX */ ++ ++#ifdef CONFIG_IEEE80211AC + if (hapd->conf->vendor_vht) + pos = hostapd_eid_vendor_vht(hapd, pos); + #endif /* CONFIG_IEEE80211AC */ + ++ /* WPA */ ++ if (hapd->conf->wpa == WPA_PROTO_WPA) ++ pos = hostapd_eid_wpa(hapd, pos, epos - pos); ++ + /* Wi-Fi Alliance WMM */ + pos = hostapd_eid_wmm(hapd, pos); + +@@ -501,6 +554,9 @@ + pos = hostapd_eid_osen(hapd, pos); + #endif /* CONFIG_HS20 */ + ++ pos = hostapd_eid_mbo(hapd, pos, (u8 *) resp + buflen - pos); ++ pos = hostapd_eid_owe_trans(hapd, pos, (u8 *) resp + buflen - pos); ++ + if (hapd->conf->vendor_elements) { + os_memcpy(pos, wpabuf_head(hapd->conf->vendor_elements), + wpabuf_len(hapd->conf->vendor_elements)); +@@ -537,8 +593,8 @@ + + pos = ssid_list; + end = ssid_list + ssid_list_len; +- while (pos + 1 <= end) { +- if (pos + 2 + pos[1] > end) ++ while (end - pos >= 1) { ++ if (2 + pos[1] > end - pos) + break; + if (pos[1] == 0) + wildcard = 1; +@@ -574,7 +630,7 @@ + MAC2STR(info->addr)); + dl_list_del(&info->list); + iface->num_sta_seen--; +- os_free(info); ++ sta_track_del(info); + } + } + +@@ -592,7 +648,7 @@ + } + + +-void sta_track_add(struct hostapd_iface *iface, const u8 *addr) ++void sta_track_add(struct hostapd_iface *iface, const u8 *addr, int ssi_signal) + { + struct hostapd_sta_info *info; + +@@ -602,13 +658,17 @@ + dl_list_del(&info->list); + dl_list_add_tail(&iface->sta_seen, &info->list); + os_get_reltime(&info->last_seen); ++ info->ssi_signal = ssi_signal; + return; + } + + /* Add a new entry */ + info = os_zalloc(sizeof(*info)); ++ if (info == NULL) ++ return; + os_memcpy(info->addr, addr, ETH_ALEN); + os_get_reltime(&info->last_seen); ++ info->ssi_signal = ssi_signal; + + if (iface->num_sta_seen >= iface->conf->track_sta_max_num) { + /* Expire oldest entry to make room for a new one */ +@@ -648,6 +708,23 @@ + } + + ++#ifdef CONFIG_TAXONOMY ++void sta_track_claim_taxonomy_info(struct hostapd_iface *iface, const u8 *addr, ++ struct wpabuf **probe_ie_taxonomy) ++{ ++ struct hostapd_sta_info *info; ++ ++ info = sta_track_get(iface, addr); ++ if (!info) ++ return; ++ ++ wpabuf_free(*probe_ie_taxonomy); ++ *probe_ie_taxonomy = info->probe_ie_taxonomy; ++ info->probe_ie_taxonomy = NULL; ++} ++#endif /* CONFIG_TAXONOMY */ ++ ++ + void handle_probe_req(struct hostapd_data *hapd, + const struct ieee80211_mgmt *mgmt, size_t len, + int ssi_signal) +@@ -659,14 +736,33 @@ + size_t i, resp_len; + int noack; + enum ssid_match_result res; ++ int ret; ++ u16 csa_offs[2]; ++ size_t csa_offs_len; ++ u32 session_timeout, acct_interim_interval; ++ struct vlan_description vlan_id; ++ struct hostapd_sta_wpa_psk_short *psk = NULL; ++ char *identity = NULL; ++ char *radius_cui = NULL; + +- ie = mgmt->u.probe_req.variable; +- if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.probe_req)) ++ if (len < IEEE80211_HDRLEN) + return; ++ ie = ((const u8 *) mgmt) + IEEE80211_HDRLEN; + if (hapd->iconf->track_sta_max_num) +- sta_track_add(hapd->iface, mgmt->sa); +- ie_len = len - (IEEE80211_HDRLEN + sizeof(mgmt->u.probe_req)); ++ sta_track_add(hapd->iface, mgmt->sa, ssi_signal); ++ ie_len = len - IEEE80211_HDRLEN; + ++ ret = ieee802_11_allowed_address(hapd, mgmt->sa, (const u8 *) mgmt, len, ++ &session_timeout, ++ &acct_interim_interval, &vlan_id, ++ &psk, &identity, &radius_cui, 1); ++ if (ret == HOSTAPD_ACL_REJECT) { ++ wpa_msg(hapd->msg_ctx, MSG_DEBUG, ++ "Ignore Probe Request frame from " MACSTR ++ " due to ACL reject ", MAC2STR(mgmt->sa)); ++ return; ++ } ++ + for (i = 0; hapd->probereq_cb && i < hapd->num_probereq_cb; i++) + if (hapd->probereq_cb[i].cb(hapd->probereq_cb[i].ctx, + mgmt->sa, mgmt->da, mgmt->bssid, +@@ -673,7 +769,7 @@ + ie, ie_len, ssi_signal) > 0) + return; + +- if (!hapd->iconf->send_probe_response) ++ if (!hapd->conf->send_probe_response) + return; + + if (ieee802_11_parse_elems(ie, ie_len, &elems, 0) == ParseFailed) { +@@ -711,7 +807,7 @@ + } + + #ifdef CONFIG_P2P +- if (hapd->p2p && elems.wps_ie) { ++ if (hapd->p2p && hapd->p2p_group && elems.wps_ie) { + struct wpabuf *wps; + wps = ieee802_11_vendor_ie_concat(ie, ie_len, WPS_DEV_OUI_WFA); + if (wps && !p2p_group_match_dev_type(hapd->p2p_group, wps)) { +@@ -724,7 +820,7 @@ + wpabuf_free(wps); + } + +- if (hapd->p2p && elems.p2p) { ++ if (hapd->p2p && hapd->p2p_group && elems.p2p) { + struct wpabuf *p2p; + p2p = ieee802_11_vendor_ie_concat(ie, ie_len, P2P_IE_VENDOR_TYPE); + if (p2p && !p2p_group_match_dev_id(hapd->p2p_group, p2p)) { +@@ -754,6 +850,21 @@ + } + #endif /* CONFIG_P2P */ + ++#ifdef CONFIG_TAXONOMY ++ { ++ struct sta_info *sta; ++ struct hostapd_sta_info *info; ++ ++ if ((sta = ap_get_sta(hapd, mgmt->sa)) != NULL) { ++ taxonomy_sta_info_probe_req(hapd, sta, ie, ie_len); ++ } else if ((info = sta_track_get(hapd->iface, ++ mgmt->sa)) != NULL) { ++ taxonomy_hostapd_sta_info_probe_req(hapd, info, ++ ie, ie_len); ++ } ++ } ++#endif /* CONFIG_TAXONOMY */ ++ + res = ssid_match(hapd, elems.ssid, elems.ssid_len, + elems.ssid_list, elems.ssid_list_len); + if (res == NO_SSID_MATCH) { +@@ -825,6 +936,17 @@ + return; + } + ++ if (hapd->conf->no_probe_resp_if_max_sta && ++ is_multicast_ether_addr(mgmt->da) && ++ is_multicast_ether_addr(mgmt->bssid) && ++ hapd->num_sta >= hapd->conf->max_num_sta && ++ !ap_get_sta(hapd, mgmt->sa)) { ++ wpa_printf(MSG_MSGDUMP, "%s: Ignore Probe Request from " MACSTR ++ " since no room for additional STA", ++ hapd->conf->iface, MAC2STR(mgmt->sa)); ++ return; ++ } ++ + #ifdef CONFIG_TESTING_OPTIONS + if (hapd->iconf->ignore_probe_probability > 0.0 && + drand48() < hapd->iconf->ignore_probe_probability) { +@@ -835,6 +957,9 @@ + } + #endif /* CONFIG_TESTING_OPTIONS */ + ++ wpa_msg_ctrl(hapd->msg_ctx, MSG_INFO, RX_PROBE_REQUEST "sa=" MACSTR ++ " signal=%d", MAC2STR(mgmt->sa), ssi_signal); ++ + resp = hostapd_gen_probe_resp(hapd, mgmt, elems.p2p != NULL, + &resp_len); + if (resp == NULL) +@@ -847,7 +972,22 @@ + noack = !!(res == WILDCARD_SSID_MATCH && + is_broadcast_ether_addr(mgmt->da)); + +- if (hostapd_drv_send_mlme(hapd, resp, resp_len, noack) < 0) ++ csa_offs_len = 0; ++ if (hapd->csa_in_progress) { ++ if (hapd->cs_c_off_proberesp) ++ csa_offs[csa_offs_len++] = ++ hapd->cs_c_off_proberesp; ++ ++ if (hapd->cs_c_off_ecsa_proberesp) ++ csa_offs[csa_offs_len++] = ++ hapd->cs_c_off_ecsa_proberesp; ++ } ++ ++ ret = hostapd_drv_send_mlme_csa(hapd, resp, resp_len, noack, ++ csa_offs_len ? csa_offs : NULL, ++ csa_offs_len); ++ ++ if (ret < 0) + wpa_printf(MSG_INFO, "handle_probe_req: send failed"); + + os_free(resp); +@@ -896,6 +1036,16 @@ + #endif /* NEED_AP_MLME */ + + ++void sta_track_del(struct hostapd_sta_info *info) ++{ ++#ifdef CONFIG_TAXONOMY ++ wpabuf_free(info->probe_ie_taxonomy); ++ info->probe_ie_taxonomy = NULL; ++#endif /* CONFIG_TAXONOMY */ ++ os_free(info); ++} ++ ++ + int ieee802_11_build_ap_params(struct hostapd_data *hapd, + struct wpa_driver_ap_params *params) + { +@@ -906,7 +1056,7 @@ + size_t resp_len = 0; + #ifdef NEED_AP_MLME + u16 capab_info; +- u8 *pos, *tailpos; ++ u8 *pos, *tailpos, *csa_pos; + + #define BEACON_HEAD_BUF_SIZE 256 + #define BEACON_TAIL_BUF_SIZE 512 +@@ -934,6 +1084,17 @@ + } + #endif /* CONFIG_IEEE80211AC */ + ++#ifdef CONFIG_IEEE80211AX ++ if (hapd->iconf->ieee80211ax) { ++ tail_len += 3 + sizeof(struct ieee80211_he_capabilities) + ++ 3 + sizeof(struct ieee80211_he_operation) + ++ 3 + sizeof(struct ieee80211_he_mu_edca_parameter_set); ++ } ++#endif /* CONFIG_IEEE80211AX */ ++ ++ tail_len += hostapd_mbo_ie_len(hapd); ++ tail_len += hostapd_eid_owe_trans_len(hapd); ++ + tailpos = tail = os_malloc(tail_len); + if (head == NULL || tail == NULL) { + wpa_printf(MSG_ERROR, "Failed to set beacon data"); +@@ -987,6 +1148,12 @@ + /* Power Constraint element */ + tailpos = hostapd_eid_pwr_constraint(hapd, tailpos); + ++ /* CSA IE */ ++ csa_pos = hostapd_eid_csa(hapd, tailpos); ++ if (csa_pos != tailpos) ++ hapd->cs_c_off_beacon = csa_pos - tail - 1; ++ tailpos = csa_pos; ++ + /* ERP Information element */ + tailpos = hostapd_eid_erp_info(hapd, tailpos); + +@@ -993,9 +1160,11 @@ + /* Extended supported rates */ + tailpos = hostapd_eid_ext_supp_rates(hapd, tailpos); + +- /* RSN, MDIE, WPA */ +- tailpos = hostapd_eid_wpa(hapd, tailpos, tail + BEACON_TAIL_BUF_SIZE - +- tailpos); ++ /* RSN, MDIE */ ++ if (hapd->conf->wpa != WPA_PROTO_WPA) ++ tailpos = hostapd_eid_wpa(hapd, tailpos, ++ tail + BEACON_TAIL_BUF_SIZE - ++ tailpos); + + tailpos = hostapd_eid_rm_enabled_capab(hapd, tailpos, + tail + BEACON_TAIL_BUF_SIZE - +@@ -1004,7 +1173,19 @@ + tailpos = hostapd_eid_bss_load(hapd, tailpos, + tail + BEACON_TAIL_BUF_SIZE - tailpos); + ++ /* eCSA IE */ ++ csa_pos = hostapd_eid_ecsa(hapd, tailpos); ++ if (csa_pos != tailpos) ++ hapd->cs_c_off_ecsa_beacon = csa_pos - tail - 1; ++ tailpos = csa_pos; ++ ++ tailpos = hostapd_eid_supported_op_classes(hapd, tailpos); ++ + #ifdef CONFIG_IEEE80211N ++ /* Secondary Channel Offset element */ ++ /* TODO: The standard doesn't specify a position for this element. */ ++ tailpos = hostapd_eid_secondary_channel(hapd, tailpos); ++ + tailpos = hostapd_eid_ht_capabilities(hapd, tailpos); + tailpos = hostapd_eid_ht_operation(hapd, tailpos); + #endif /* CONFIG_IEEE80211N */ +@@ -1020,8 +1201,6 @@ + tailpos = hostapd_eid_interworking(hapd, tailpos); + tailpos = hostapd_eid_adv_proto(hapd, tailpos); + tailpos = hostapd_eid_roaming_consortium(hapd, tailpos); +- tailpos = hostapd_add_csa_elems(hapd, tailpos, tail, +- &hapd->cs_c_off_beacon); + + #ifdef CONFIG_FST + if (hapd->iface->fst_ies) { +@@ -1033,13 +1212,34 @@ + + #ifdef CONFIG_IEEE80211AC + if (hapd->iconf->ieee80211ac && !hapd->conf->disable_11ac) { +- tailpos = hostapd_eid_vht_capabilities(hapd, tailpos); ++ tailpos = hostapd_eid_vht_capabilities(hapd, tailpos, 0); + tailpos = hostapd_eid_vht_operation(hapd, tailpos); ++ tailpos = hostapd_eid_txpower_envelope(hapd, tailpos); ++ tailpos = hostapd_eid_wb_chsw_wrapper(hapd, tailpos); + } ++#endif /* CONFIG_IEEE80211AC */ ++ ++ tailpos = hostapd_eid_fils_indic(hapd, tailpos, 0); ++ ++#ifdef CONFIG_IEEE80211AX ++ if (hapd->iconf->ieee80211ax) { ++ tailpos = hostapd_eid_he_capab(hapd, tailpos); ++ tailpos = hostapd_eid_he_operation(hapd, tailpos); ++ tailpos = hostapd_eid_he_mu_edca_parameter_set(hapd, tailpos); ++ } ++#endif /* CONFIG_IEEE80211AX */ ++ ++#ifdef CONFIG_IEEE80211AC + if (hapd->conf->vendor_vht) + tailpos = hostapd_eid_vendor_vht(hapd, tailpos); + #endif /* CONFIG_IEEE80211AC */ + ++ /* WPA */ ++ if (hapd->conf->wpa == WPA_PROTO_WPA) ++ tailpos = hostapd_eid_wpa(hapd, tailpos, ++ tail + BEACON_TAIL_BUF_SIZE - ++ tailpos); ++ + /* Wi-Fi Alliance WMM */ + tailpos = hostapd_eid_wmm(hapd, tailpos); + +@@ -1069,6 +1269,10 @@ + tailpos = hostapd_eid_osen(hapd, tailpos); + #endif /* CONFIG_HS20 */ + ++ tailpos = hostapd_eid_mbo(hapd, tailpos, tail + tail_len - tailpos); ++ tailpos = hostapd_eid_owe_trans(hapd, tailpos, ++ tail + tail_len - tailpos); ++ + if (hapd->conf->vendor_elements) { + os_memcpy(tailpos, wpabuf_head(hapd->conf->vendor_elements), + wpabuf_len(hapd->conf->vendor_elements)); +@@ -1090,6 +1294,8 @@ + params->dtim_period = hapd->conf->dtim_period; + params->beacon_int = hapd->iconf->beacon_int; + params->basic_rates = hapd->iface->basic_rates; ++ params->beacon_rate = hapd->iconf->beacon_rate; ++ params->rate_type = hapd->iconf->rate_type; + params->ssid = hapd->conf->ssid.ssid; + params->ssid_len = hapd->conf->ssid.ssid_len; + if ((hapd->conf->wpa & (WPA_PROTO_WPA | WPA_PROTO_RSN)) == +@@ -1153,6 +1359,20 @@ + params->osen = 1; + } + #endif /* CONFIG_HS20 */ ++ params->multicast_to_unicast = hapd->conf->multicast_to_unicast; ++ params->pbss = hapd->conf->pbss; ++ ++ if (hapd->conf->ftm_responder) { ++ if (hapd->iface->drv_flags & WPA_DRIVER_FLAGS_FTM_RESPONDER) { ++ params->ftm_responder = 1; ++ params->lci = hapd->iface->conf->lci; ++ params->civic = hapd->iface->conf->civic; ++ } else { ++ wpa_printf(MSG_WARNING, ++ "Not configuring FTM responder as the driver doesn't advertise support for it"); ++ } ++ } ++ + return 0; + } + +--- contrib/wpa/src/ap/beacon.h.orig ++++ contrib/wpa/src/ap/beacon.h +@@ -21,10 +21,13 @@ + int ieee802_11_build_ap_params(struct hostapd_data *hapd, + struct wpa_driver_ap_params *params); + void ieee802_11_free_ap_params(struct wpa_driver_ap_params *params); +-void sta_track_add(struct hostapd_iface *iface, const u8 *addr); ++void sta_track_add(struct hostapd_iface *iface, const u8 *addr, int ssi_signal); ++void sta_track_del(struct hostapd_sta_info *info); + void sta_track_expire(struct hostapd_iface *iface, int force); + struct hostapd_data * + sta_track_seen_on(struct hostapd_iface *iface, const u8 *addr, + const char *ifname); ++void sta_track_claim_taxonomy_info(struct hostapd_iface *iface, const u8 *addr, ++ struct wpabuf **probe_ie_taxonomy); + + #endif /* BEACON_H */ +--- contrib/wpa/src/ap/bss_load.c.orig ++++ contrib/wpa/src/ap/bss_load.c +@@ -16,11 +16,35 @@ + #include "beacon.h" + + ++static int get_bss_load_update_timeout(struct hostapd_data *hapd, ++ unsigned int *sec, unsigned int *usec) ++{ ++ unsigned int update_period = hapd->conf->bss_load_update_period; ++ unsigned int beacon_int = hapd->iconf->beacon_int; ++ unsigned int update_timeout; ++ ++ if (!update_period || !beacon_int) { ++ wpa_printf(MSG_ERROR, ++ "BSS Load: Invalid BSS load update configuration (period=%u beacon_int=%u)", ++ update_period, beacon_int); ++ return -1; ++ } ++ ++ update_timeout = update_period * beacon_int; ++ ++ *sec = ((update_timeout / 1000) * 1024) / 1000; ++ *usec = (update_timeout % 1000) * 1024; ++ ++ return 0; ++} ++ ++ + static void update_channel_utilization(void *eloop_data, void *user_data) + { + struct hostapd_data *hapd = eloop_data; + unsigned int sec, usec; + int err; ++ struct hostapd_iface *iface = hapd->iface; + + if (!(hapd->beacon_set_done && hapd->started)) + return; +@@ -33,8 +57,24 @@ + + ieee802_11_set_beacon(hapd); + +- sec = ((hapd->bss_load_update_timeout / 1000) * 1024) / 1000; +- usec = (hapd->bss_load_update_timeout % 1000) * 1024; ++ if (get_bss_load_update_timeout(hapd, &sec, &usec) < 0) ++ return; ++ ++ if (hapd->conf->chan_util_avg_period) { ++ iface->chan_util_samples_sum += iface->channel_utilization; ++ iface->chan_util_num_sample_periods += ++ hapd->conf->bss_load_update_period; ++ if (iface->chan_util_num_sample_periods >= ++ hapd->conf->chan_util_avg_period) { ++ iface->chan_util_average = ++ iface->chan_util_samples_sum / ++ (iface->chan_util_num_sample_periods / ++ hapd->conf->bss_load_update_period); ++ iface->chan_util_samples_sum = 0; ++ iface->chan_util_num_sample_periods = 0; ++ } ++ } ++ + eloop_register_timeout(sec, usec, update_channel_utilization, hapd, + NULL); + } +@@ -42,17 +82,11 @@ + + int bss_load_update_init(struct hostapd_data *hapd) + { +- struct hostapd_bss_config *conf = hapd->conf; +- struct hostapd_config *iconf = hapd->iconf; + unsigned int sec, usec; + +- if (!conf->bss_load_update_period || !iconf->beacon_int) ++ if (get_bss_load_update_timeout(hapd, &sec, &usec) < 0) + return -1; + +- hapd->bss_load_update_timeout = conf->bss_load_update_period * +- iconf->beacon_int; +- sec = ((hapd->bss_load_update_timeout / 1000) * 1024) / 1000; +- usec = (hapd->bss_load_update_timeout % 1000) * 1024; + eloop_register_timeout(sec, usec, update_channel_utilization, hapd, + NULL); + return 0; +--- contrib/wpa/src/ap/ctrl_iface_ap.c.orig ++++ contrib/wpa/src/ap/ctrl_iface_ap.c +@@ -1,6 +1,6 @@ + /* + * Control interface for shared AP commands +- * Copyright (c) 2004-2014, Jouni Malinen ++ * Copyright (c) 2004-2019, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. +@@ -22,8 +22,34 @@ + #include "p2p_hostapd.h" + #include "ctrl_iface_ap.h" + #include "ap_drv_ops.h" ++#include "mbo_ap.h" ++#include "taxonomy.h" + + ++static size_t hostapd_write_ht_mcs_bitmask(char *buf, size_t buflen, ++ size_t curr_len, const u8 *mcs_set) ++{ ++ int ret; ++ size_t len = curr_len; ++ ++ ret = os_snprintf(buf + len, buflen - len, ++ "ht_mcs_bitmask="); ++ if (os_snprintf_error(buflen - len, ret)) ++ return len; ++ len += ret; ++ ++ /* 77 first bits (+ 3 reserved bits) */ ++ len += wpa_snprintf_hex(buf + len, buflen - len, mcs_set, 10); ++ ++ ret = os_snprintf(buf + len, buflen - len, "\n"); ++ if (os_snprintf_error(buflen - len, ret)) ++ return curr_len; ++ len += ret; ++ ++ return len; ++} ++ ++ + static int hostapd_get_sta_tx_rx(struct hostapd_data *hapd, + struct sta_info *sta, + char *buf, size_t buflen) +@@ -30,17 +56,111 @@ + { + struct hostap_sta_driver_data data; + int ret; ++ int len = 0; + + if (hostapd_drv_read_sta_data(hapd, &data, sta->addr) < 0) + return 0; + + ret = os_snprintf(buf, buflen, "rx_packets=%lu\ntx_packets=%lu\n" +- "rx_bytes=%lu\ntx_bytes=%lu\n", ++ "rx_bytes=%llu\ntx_bytes=%llu\ninactive_msec=%lu\n" ++ "signal=%d\n", + data.rx_packets, data.tx_packets, +- data.rx_bytes, data.tx_bytes); ++ data.rx_bytes, data.tx_bytes, data.inactive_msec, ++ data.signal); + if (os_snprintf_error(buflen, ret)) + return 0; +- return ret; ++ len += ret; ++ ++ ret = os_snprintf(buf + len, buflen - len, "rx_rate_info=%lu", ++ data.current_rx_rate); ++ if (os_snprintf_error(buflen - len, ret)) ++ return len; ++ len += ret; ++ if (data.flags & STA_DRV_DATA_RX_MCS) { ++ ret = os_snprintf(buf + len, buflen - len, " mcs %u", ++ data.rx_mcs); ++ if (!os_snprintf_error(buflen - len, ret)) ++ len += ret; ++ } ++ if (data.flags & STA_DRV_DATA_RX_VHT_MCS) { ++ ret = os_snprintf(buf + len, buflen - len, " vhtmcs %u", ++ data.rx_vhtmcs); ++ if (!os_snprintf_error(buflen - len, ret)) ++ len += ret; ++ } ++ if (data.flags & STA_DRV_DATA_RX_VHT_NSS) { ++ ret = os_snprintf(buf + len, buflen - len, " vhtnss %u", ++ data.rx_vht_nss); ++ if (!os_snprintf_error(buflen - len, ret)) ++ len += ret; ++ } ++ if (data.flags & STA_DRV_DATA_RX_SHORT_GI) { ++ ret = os_snprintf(buf + len, buflen - len, " shortGI"); ++ if (!os_snprintf_error(buflen - len, ret)) ++ len += ret; ++ } ++ ret = os_snprintf(buf + len, buflen - len, "\n"); ++ if (!os_snprintf_error(buflen - len, ret)) ++ len += ret; ++ ++ ret = os_snprintf(buf + len, buflen - len, "tx_rate_info=%lu", ++ data.current_tx_rate); ++ if (os_snprintf_error(buflen - len, ret)) ++ return len; ++ len += ret; ++ if (data.flags & STA_DRV_DATA_TX_MCS) { ++ ret = os_snprintf(buf + len, buflen - len, " mcs %u", ++ data.tx_mcs); ++ if (!os_snprintf_error(buflen - len, ret)) ++ len += ret; ++ } ++ if (data.flags & STA_DRV_DATA_TX_VHT_MCS) { ++ ret = os_snprintf(buf + len, buflen - len, " vhtmcs %u", ++ data.tx_vhtmcs); ++ if (!os_snprintf_error(buflen - len, ret)) ++ len += ret; ++ } ++ if (data.flags & STA_DRV_DATA_TX_VHT_NSS) { ++ ret = os_snprintf(buf + len, buflen - len, " vhtnss %u", ++ data.tx_vht_nss); ++ if (!os_snprintf_error(buflen - len, ret)) ++ len += ret; ++ } ++ if (data.flags & STA_DRV_DATA_TX_SHORT_GI) { ++ ret = os_snprintf(buf + len, buflen - len, " shortGI"); ++ if (!os_snprintf_error(buflen - len, ret)) ++ len += ret; ++ } ++ ret = os_snprintf(buf + len, buflen - len, "\n"); ++ if (!os_snprintf_error(buflen - len, ret)) ++ len += ret; ++ ++ if ((sta->flags & WLAN_STA_VHT) && sta->vht_capabilities) { ++ ret = os_snprintf(buf + len, buflen - len, ++ "rx_vht_mcs_map=%04x\n" ++ "tx_vht_mcs_map=%04x\n", ++ le_to_host16(sta->vht_capabilities-> ++ vht_supported_mcs_set.rx_map), ++ le_to_host16(sta->vht_capabilities-> ++ vht_supported_mcs_set.tx_map)); ++ if (!os_snprintf_error(buflen - len, ret)) ++ len += ret; ++ } ++ ++ if ((sta->flags & WLAN_STA_HT) && sta->ht_capabilities) { ++ len = hostapd_write_ht_mcs_bitmask(buf, buflen, len, ++ sta->ht_capabilities-> ++ supported_mcs_set); ++ } ++ ++ if (data.flags & STA_DRV_DATA_LAST_ACK_RSSI) { ++ ret = os_snprintf(buf + len, buflen - len, ++ "last_ack_signal=%d\n", data.last_ack_rssi); ++ if (!os_snprintf_error(buflen - len, ret)) ++ len += ret; ++ } ++ ++ return len; + } + + +@@ -87,6 +207,7 @@ + char *buf, size_t buflen) + { + int len, res, ret, i; ++ const char *keyid; + + if (!sta) + return 0; +@@ -161,6 +282,73 @@ + len += res; + } + ++ res = mbo_ap_get_info(sta, buf + len, buflen - len); ++ if (res >= 0) ++ len += res; ++ ++ if (sta->supp_op_classes && ++ buflen - len > (unsigned) (17 + 2 * sta->supp_op_classes[0])) { ++ len += os_snprintf(buf + len, buflen - len, "supp_op_classes="); ++ len += wpa_snprintf_hex(buf + len, buflen - len, ++ sta->supp_op_classes + 1, ++ sta->supp_op_classes[0]); ++ len += os_snprintf(buf + len, buflen - len, "\n"); ++ } ++ ++ if (sta->power_capab) { ++ ret = os_snprintf(buf + len, buflen - len, ++ "min_txpower=%d\n" ++ "max_txpower=%d\n", ++ sta->min_tx_power, sta->max_tx_power); ++ if (!os_snprintf_error(buflen - len, ret)) ++ len += ret; ++ } ++ ++#ifdef CONFIG_IEEE80211AC ++ if ((sta->flags & WLAN_STA_VHT) && sta->vht_capabilities) { ++ res = os_snprintf(buf + len, buflen - len, ++ "vht_caps_info=0x%08x\n", ++ le_to_host32(sta->vht_capabilities-> ++ vht_capabilities_info)); ++ if (!os_snprintf_error(buflen - len, res)) ++ len += res; ++ } ++#endif /* CONFIG_IEEE80211AC */ ++ ++#ifdef CONFIG_IEEE80211N ++ if ((sta->flags & WLAN_STA_HT) && sta->ht_capabilities) { ++ res = os_snprintf(buf + len, buflen - len, ++ "ht_caps_info=0x%04x\n", ++ le_to_host16(sta->ht_capabilities-> ++ ht_capabilities_info)); ++ if (!os_snprintf_error(buflen - len, res)) ++ len += res; ++ } ++#endif /* CONFIG_IEEE80211N */ ++ ++ if (sta->ext_capability && ++ buflen - len > (unsigned) (11 + 2 * sta->ext_capability[0])) { ++ len += os_snprintf(buf + len, buflen - len, "ext_capab="); ++ len += wpa_snprintf_hex(buf + len, buflen - len, ++ sta->ext_capability + 1, ++ sta->ext_capability[0]); ++ len += os_snprintf(buf + len, buflen - len, "\n"); ++ } ++ ++ if (sta->flags & WLAN_STA_WDS && sta->ifname_wds) { ++ ret = os_snprintf(buf + len, buflen - len, ++ "wds_sta_ifname=%s\n", sta->ifname_wds); ++ if (!os_snprintf_error(buflen - len, ret)) ++ len += ret; ++ } ++ ++ keyid = ap_sta_wpa_get_keyid(hapd, sta); ++ if (keyid) { ++ ret = os_snprintf(buf + len, buflen - len, "keyid=%s\n", keyid); ++ if (!os_snprintf_error(buflen - len, ret)) ++ len += ret; ++ } ++ + return len; + } + +@@ -244,7 +432,7 @@ + int ret; + u8 *pos; + +- if (hapd->driver->send_frame == NULL) ++ if (!hapd->drv_priv || !hapd->driver->send_frame) + return -1; + + mgmt = os_zalloc(sizeof(*mgmt) + 100); +@@ -255,7 +443,7 @@ + wpa_dbg(hapd->msg_ctx, MSG_DEBUG, "P2P: Disconnect STA " MACSTR + " with minor reason code %u (stype=%u (%s))", + MAC2STR(addr), minor_reason_code, stype, +- fc2str(mgmt->frame_control)); ++ fc2str(le_to_host16(mgmt->frame_control))); + + os_memcpy(mgmt->da, addr, ETH_ALEN); + os_memcpy(mgmt->sa, hapd->own_addr, ETH_ALEN); +@@ -263,11 +451,11 @@ + if (stype == WLAN_FC_STYPE_DEAUTH) { + mgmt->u.deauth.reason_code = + host_to_le16(WLAN_REASON_PREV_AUTH_NOT_VALID); +- pos = (u8 *) (&mgmt->u.deauth.reason_code + 1); ++ pos = mgmt->u.deauth.variable; + } else { + mgmt->u.disassoc.reason_code = + host_to_le16(WLAN_REASON_PREV_AUTH_NOT_VALID); +- pos = (u8 *) (&mgmt->u.disassoc.reason_code + 1); ++ pos = mgmt->u.disassoc.variable; + } + + *pos++ = WLAN_EID_VENDOR_SPECIFIC; +@@ -311,7 +499,7 @@ + if (pos) { + struct ieee80211_mgmt mgmt; + int encrypt; +- if (hapd->driver->send_frame == NULL) ++ if (!hapd->drv_priv || !hapd->driver->send_frame) + return -1; + pos += 6; + encrypt = atoi(pos); +@@ -338,7 +526,10 @@ + } + #endif /* CONFIG_P2P_MANAGER */ + +- hostapd_drv_sta_deauth(hapd, addr, reason); ++ if (os_strstr(txtaddr, " tx=0")) ++ hostapd_drv_sta_remove(hapd, addr); ++ else ++ hostapd_drv_sta_deauth(hapd, addr, reason); + sta = ap_get_sta(hapd, addr); + if (sta) + ap_sta_deauthenticate(hapd, sta, reason); +@@ -371,7 +562,7 @@ + if (pos) { + struct ieee80211_mgmt mgmt; + int encrypt; +- if (hapd->driver->send_frame == NULL) ++ if (!hapd->drv_priv || !hapd->driver->send_frame) + return -1; + pos += 6; + encrypt = atoi(pos); +@@ -398,7 +589,10 @@ + } + #endif /* CONFIG_P2P_MANAGER */ + +- hostapd_drv_sta_disassoc(hapd, addr, reason); ++ if (os_strstr(txtaddr, " tx=0")) ++ hostapd_drv_sta_remove(hapd, addr); ++ else ++ hostapd_drv_sta_disassoc(hapd, addr, reason); + sta = ap_get_sta(hapd, addr); + if (sta) + ap_sta_disassociate(hapd, sta, reason); +@@ -409,11 +603,55 @@ + } + + ++#ifdef CONFIG_TAXONOMY ++int hostapd_ctrl_iface_signature(struct hostapd_data *hapd, ++ const char *txtaddr, ++ char *buf, size_t buflen) ++{ ++ u8 addr[ETH_ALEN]; ++ struct sta_info *sta; ++ ++ wpa_dbg(hapd->msg_ctx, MSG_DEBUG, "CTRL_IFACE SIGNATURE %s", txtaddr); ++ ++ if (hwaddr_aton(txtaddr, addr)) ++ return -1; ++ ++ sta = ap_get_sta(hapd, addr); ++ if (!sta) ++ return -1; ++ ++ return retrieve_sta_taxonomy(hapd, sta, buf, buflen); ++} ++#endif /* CONFIG_TAXONOMY */ ++ ++ ++int hostapd_ctrl_iface_poll_sta(struct hostapd_data *hapd, ++ const char *txtaddr) ++{ ++ u8 addr[ETH_ALEN]; ++ struct sta_info *sta; ++ ++ wpa_dbg(hapd->msg_ctx, MSG_DEBUG, "CTRL_IFACE POLL_STA %s", txtaddr); ++ ++ if (hwaddr_aton(txtaddr, addr)) ++ return -1; ++ ++ sta = ap_get_sta(hapd, addr); ++ if (!sta) ++ return -1; ++ ++ hostapd_drv_poll_client(hapd, hapd->own_addr, addr, ++ sta->flags & WLAN_STA_WMM); ++ return 0; ++} ++ ++ + int hostapd_ctrl_iface_status(struct hostapd_data *hapd, char *buf, + size_t buflen) + { + struct hostapd_iface *iface = hapd->iface; +- int len = 0, ret; ++ struct hostapd_hw_modes *mode = iface->current_mode; ++ int len = 0, ret, j; + size_t i; + + ret = os_snprintf(buf + len, buflen - len, +@@ -474,20 +712,93 @@ + "secondary_channel=%d\n" + "ieee80211n=%d\n" + "ieee80211ac=%d\n" +- "vht_oper_chwidth=%d\n" +- "vht_oper_centr_freq_seg0_idx=%d\n" +- "vht_oper_centr_freq_seg1_idx=%d\n", ++ "beacon_int=%u\n" ++ "dtim_period=%d\n", + iface->conf->channel, +- iface->conf->secondary_channel, +- iface->conf->ieee80211n, +- iface->conf->ieee80211ac, +- iface->conf->vht_oper_chwidth, +- iface->conf->vht_oper_centr_freq_seg0_idx, +- iface->conf->vht_oper_centr_freq_seg1_idx); ++ iface->conf->ieee80211n && !hapd->conf->disable_11n ? ++ iface->conf->secondary_channel : 0, ++ iface->conf->ieee80211n && !hapd->conf->disable_11n, ++ iface->conf->ieee80211ac && ++ !hapd->conf->disable_11ac, ++ iface->conf->beacon_int, ++ hapd->conf->dtim_period); + if (os_snprintf_error(buflen - len, ret)) + return len; + len += ret; ++ if (iface->conf->ieee80211ac && !hapd->conf->disable_11ac) { ++ ret = os_snprintf(buf + len, buflen - len, ++ "vht_oper_chwidth=%d\n" ++ "vht_oper_centr_freq_seg0_idx=%d\n" ++ "vht_oper_centr_freq_seg1_idx=%d\n" ++ "vht_caps_info=%08x\n", ++ iface->conf->vht_oper_chwidth, ++ iface->conf->vht_oper_centr_freq_seg0_idx, ++ iface->conf->vht_oper_centr_freq_seg1_idx, ++ iface->conf->vht_capab); ++ if (os_snprintf_error(buflen - len, ret)) ++ return len; ++ len += ret; ++ } + ++ if (iface->conf->ieee80211ac && !hapd->conf->disable_11ac && mode) { ++ u16 rxmap = WPA_GET_LE16(&mode->vht_mcs_set[0]); ++ u16 txmap = WPA_GET_LE16(&mode->vht_mcs_set[4]); ++ ++ ret = os_snprintf(buf + len, buflen - len, ++ "rx_vht_mcs_map=%04x\n" ++ "tx_vht_mcs_map=%04x\n", ++ rxmap, txmap); ++ if (os_snprintf_error(buflen - len, ret)) ++ return len; ++ len += ret; ++ } ++ ++ if (iface->conf->ieee80211n && !hapd->conf->disable_11n) { ++ ret = os_snprintf(buf + len, buflen - len, ++ "ht_caps_info=%04x\n", ++ hapd->iconf->ht_capab); ++ if (os_snprintf_error(buflen - len, ret)) ++ return len; ++ len += ret; ++ } ++ ++ if (iface->conf->ieee80211n && !hapd->conf->disable_11n && mode) { ++ len = hostapd_write_ht_mcs_bitmask(buf, buflen, len, ++ mode->mcs_set); ++ } ++ ++ if (iface->current_rates && iface->num_rates) { ++ ret = os_snprintf(buf + len, buflen - len, "supported_rates="); ++ if (os_snprintf_error(buflen - len, ret)) ++ return len; ++ len += ret; ++ ++ for (j = 0; j < iface->num_rates; j++) { ++ ret = os_snprintf(buf + len, buflen - len, "%s%02x", ++ j > 0 ? " " : "", ++ iface->current_rates[j].rate / 5); ++ if (os_snprintf_error(buflen - len, ret)) ++ return len; ++ len += ret; ++ } ++ ret = os_snprintf(buf + len, buflen - len, "\n"); ++ if (os_snprintf_error(buflen - len, ret)) ++ return len; ++ len += ret; ++ } ++ ++ for (j = 0; mode && j < mode->num_channels; j++) { ++ if (mode->channels[j].freq == iface->freq) { ++ ret = os_snprintf(buf + len, buflen - len, ++ "max_txpower=%u\n", ++ mode->channels[j].max_tx_power); ++ if (os_snprintf_error(buflen - len, ret)) ++ return len; ++ len += ret; ++ break; ++ } ++ } ++ + for (i = 0; i < iface->num_bss; i++) { + struct hostapd_data *bss = iface->bss[i]; + ret = os_snprintf(buf + len, buflen - len, +@@ -506,6 +817,15 @@ + len += ret; + } + ++ if (hapd->conf->chan_util_avg_period) { ++ ret = os_snprintf(buf + len, buflen - len, ++ "chan_util_avg=%u\n", ++ iface->chan_util_average); ++ if (os_snprintf_error(buflen - len, ret)) ++ return len; ++ len += ret; ++ } ++ + return len; + } + +@@ -554,3 +874,121 @@ + { + return hostapd_drv_stop_ap(hapd); + } ++ ++ ++int hostapd_ctrl_iface_pmksa_list(struct hostapd_data *hapd, char *buf, ++ size_t len) ++{ ++ return wpa_auth_pmksa_list(hapd->wpa_auth, buf, len); ++} ++ ++ ++void hostapd_ctrl_iface_pmksa_flush(struct hostapd_data *hapd) ++{ ++ wpa_auth_pmksa_flush(hapd->wpa_auth); ++} ++ ++ ++int hostapd_ctrl_iface_pmksa_add(struct hostapd_data *hapd, char *cmd) ++{ ++ u8 spa[ETH_ALEN]; ++ u8 pmkid[PMKID_LEN]; ++ u8 pmk[PMK_LEN_MAX]; ++ size_t pmk_len; ++ char *pos, *pos2; ++ int akmp = 0, expiration = 0; ++ ++ /* ++ * Entry format: ++ * ++ */ ++ ++ if (hwaddr_aton(cmd, spa)) ++ return -1; ++ ++ pos = os_strchr(cmd, ' '); ++ if (!pos) ++ return -1; ++ pos++; ++ ++ if (hexstr2bin(pos, pmkid, PMKID_LEN) < 0) ++ return -1; ++ ++ pos = os_strchr(pos, ' '); ++ if (!pos) ++ return -1; ++ pos++; ++ ++ pos2 = os_strchr(pos, ' '); ++ if (!pos2) ++ return -1; ++ pmk_len = (pos2 - pos) / 2; ++ if (pmk_len < PMK_LEN || pmk_len > PMK_LEN_MAX || ++ hexstr2bin(pos, pmk, pmk_len) < 0) ++ return -1; ++ ++ pos = pos2 + 1; ++ ++ if (sscanf(pos, "%d %d", &expiration, &akmp) != 2) ++ return -1; ++ ++ return wpa_auth_pmksa_add2(hapd->wpa_auth, spa, pmk, pmk_len, ++ pmkid, expiration, akmp); ++} ++ ++ ++#ifdef CONFIG_PMKSA_CACHE_EXTERNAL ++#ifdef CONFIG_MESH ++ ++int hostapd_ctrl_iface_pmksa_list_mesh(struct hostapd_data *hapd, ++ const u8 *addr, char *buf, size_t len) ++{ ++ return wpa_auth_pmksa_list_mesh(hapd->wpa_auth, addr, buf, len); ++} ++ ++ ++void * hostapd_ctrl_iface_pmksa_create_entry(const u8 *aa, char *cmd) ++{ ++ u8 spa[ETH_ALEN]; ++ u8 pmkid[PMKID_LEN]; ++ u8 pmk[PMK_LEN_MAX]; ++ char *pos; ++ int expiration; ++ ++ /* ++ * Entry format: ++ * ++ */ ++ ++ if (hwaddr_aton(cmd, spa)) ++ return NULL; ++ ++ pos = os_strchr(cmd, ' '); ++ if (!pos) ++ return NULL; ++ pos++; ++ ++ if (hexstr2bin(pos, pmkid, PMKID_LEN) < 0) ++ return NULL; ++ ++ pos = os_strchr(pos, ' '); ++ if (!pos) ++ return NULL; ++ pos++; ++ ++ if (hexstr2bin(pos, pmk, PMK_LEN) < 0) ++ return NULL; ++ ++ pos = os_strchr(pos, ' '); ++ if (!pos) ++ return NULL; ++ pos++; ++ ++ if (sscanf(pos, "%d", &expiration) != 1) ++ return NULL; ++ ++ return wpa_auth_pmksa_create_entry(aa, spa, pmk, pmkid, expiration); ++} ++ ++#endif /* CONFIG_MESH */ ++#endif /* CONFIG_PMKSA_CACHE_EXTERNAL */ +--- contrib/wpa/src/ap/ctrl_iface_ap.h.orig ++++ contrib/wpa/src/ap/ctrl_iface_ap.h +@@ -19,10 +19,22 @@ + const char *txtaddr); + int hostapd_ctrl_iface_disassociate(struct hostapd_data *hapd, + const char *txtaddr); ++int hostapd_ctrl_iface_signature(struct hostapd_data *hapd, ++ const char *txtaddr, ++ char *buf, size_t buflen); ++int hostapd_ctrl_iface_poll_sta(struct hostapd_data *hapd, ++ const char *txtaddr); + int hostapd_ctrl_iface_status(struct hostapd_data *hapd, char *buf, + size_t buflen); + int hostapd_parse_csa_settings(const char *pos, + struct csa_settings *settings); + int hostapd_ctrl_iface_stop_ap(struct hostapd_data *hapd); ++int hostapd_ctrl_iface_pmksa_list(struct hostapd_data *hapd, char *buf, ++ size_t len); ++void hostapd_ctrl_iface_pmksa_flush(struct hostapd_data *hapd); ++int hostapd_ctrl_iface_pmksa_add(struct hostapd_data *hapd, char *cmd); ++int hostapd_ctrl_iface_pmksa_list_mesh(struct hostapd_data *hapd, ++ const u8 *addr, char *buf, size_t len); ++void * hostapd_ctrl_iface_pmksa_create_entry(const u8 *aa, char *cmd); + + #endif /* CTRL_IFACE_AP_H */ +--- contrib/wpa/src/ap/dfs.c.orig ++++ contrib/wpa/src/ap/dfs.c +@@ -1,7 +1,7 @@ + /* + * DFS - Dynamic Frequency Selection + * Copyright (c) 2002-2013, Jouni Malinen +- * Copyright (c) 2013-2015, Qualcomm Atheros, Inc. ++ * Copyright (c) 2013-2017, Qualcomm Atheros, Inc. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. +@@ -142,6 +142,7 @@ + { + struct hostapd_channel_data *first_chan, *chan; + int i; ++ u32 bw = num_chan_to_bw(num_chans); + + if (first_chan_idx + num_chans > mode->num_channels) + return 0; +@@ -148,6 +149,12 @@ + + first_chan = &mode->channels[first_chan_idx]; + ++ /* hostapd DFS implementation assumes the first channel as primary. ++ * If it's not allowed to use the first channel as primary, decline the ++ * whole channel range. */ ++ if (!chan_pri_allowed(first_chan)) ++ return 0; ++ + for (i = 0; i < num_chans; i++) { + chan = dfs_get_chan_data(mode, first_chan->freq + i * 20, + first_chan_idx); +@@ -154,6 +161,11 @@ + if (!chan) + return 0; + ++ /* HT 40 MHz secondary channel availability checked only for ++ * primary channel */ ++ if (!chan_bw_allowed(chan, bw, 1, !i)) ++ return 0; ++ + if (!dfs_channel_available(chan, skip_radar)) + return 0; + } +@@ -197,7 +209,8 @@ + /* Skip HT40/VHT incompatible channels */ + if (iface->conf->ieee80211n && + iface->conf->secondary_channel && +- !dfs_is_chan_allowed(chan, n_chans)) ++ (!dfs_is_chan_allowed(chan, n_chans) || ++ !(chan->allowed_bw & HOSTAPD_CHAN_WIDTH_40P))) + continue; + + /* Skip incompatible chandefs */ +@@ -450,7 +463,7 @@ + return NULL; + + if (os_get_random((u8 *) &_rand, sizeof(_rand)) < 0) +- _rand = os_random(); ++ return NULL; + chan_idx = _rand % num_available_chandefs; + dfs_find_channel(iface, &chan, chan_idx, skip_radar); + +@@ -704,7 +717,8 @@ + skip_radar); + if (!channel) { + wpa_printf(MSG_ERROR, "could not get valid channel"); +- return -1; ++ hostapd_set_state(iface, HAPD_IFACE_DFS); ++ return 0; + } + + iface->freq = channel->freq; +@@ -746,6 +760,23 @@ + } + + ++static int hostapd_config_dfs_chan_available(struct hostapd_iface *iface) ++{ ++ int n_chans, n_chans1, start_chan_idx, start_chan_idx1; ++ ++ /* Get the start (first) channel for current configuration */ ++ start_chan_idx = dfs_get_start_chan_idx(iface, &start_chan_idx1); ++ if (start_chan_idx < 0) ++ return 0; ++ ++ /* Get the number of used channels, depending on width */ ++ n_chans = dfs_get_used_n_chans(iface, &n_chans1); ++ ++ /* Check if all channels are DFS available */ ++ return dfs_check_chans_available(iface, start_chan_idx, n_chans); ++} ++ ++ + int hostapd_dfs_complete_cac(struct hostapd_iface *iface, int success, int freq, + int ht_enabled, int chan_offset, int chan_width, + int cf1, int cf2) +@@ -766,8 +797,21 @@ + set_dfs_state(iface, freq, ht_enabled, chan_offset, + chan_width, cf1, cf2, + HOSTAPD_CHAN_DFS_AVAILABLE); +- iface->cac_started = 0; +- hostapd_setup_interface_complete(iface, 0); ++ /* ++ * Just mark the channel available when CAC completion ++ * event is received in enabled state. CAC result could ++ * have been propagated from another radio having the ++ * same regulatory configuration. When CAC completion is ++ * received during non-HAPD_IFACE_ENABLED state, make ++ * sure the configured channel is available because this ++ * CAC completion event could have been propagated from ++ * another radio. ++ */ ++ if (iface->state != HAPD_IFACE_ENABLED && ++ hostapd_config_dfs_chan_available(iface)) { ++ hostapd_setup_interface_complete(iface, 0); ++ iface->cac_started = 0; ++ } + } + } + +@@ -775,6 +819,25 @@ + } + + ++int hostapd_dfs_pre_cac_expired(struct hostapd_iface *iface, int freq, ++ int ht_enabled, int chan_offset, int chan_width, ++ int cf1, int cf2) ++{ ++ wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_PRE_CAC_EXPIRED ++ "freq=%d ht_enabled=%d chan_offset=%d chan_width=%d cf1=%d cf2=%d", ++ freq, ht_enabled, chan_offset, chan_width, cf1, cf2); ++ ++ /* Proceed only if DFS is not offloaded to the driver */ ++ if (iface->drv_flags & WPA_DRIVER_FLAGS_DFS_OFFLOAD) ++ return 0; ++ ++ set_dfs_state(iface, freq, ht_enabled, chan_offset, chan_width, ++ cf1, cf2, HOSTAPD_CHAN_DFS_USABLE); ++ ++ return 0; ++} ++ ++ + static int hostapd_dfs_start_channel_switch_cac(struct hostapd_iface *iface) + { + struct hostapd_channel_data *channel; +@@ -793,7 +856,6 @@ + + if (!channel) { + wpa_printf(MSG_ERROR, "No valid channel available"); +- hostapd_setup_interface_complete(iface, err); + return err; + } + +@@ -817,16 +879,6 @@ + } + + +-static int hostapd_csa_in_progress(struct hostapd_iface *iface) +-{ +- unsigned int i; +- for (i = 0; i < iface->num_bss; i++) +- if (iface->bss[i]->csa_in_progress) +- return 1; +- return 0; +-} +- +- + static int hostapd_dfs_start_channel_switch(struct hostapd_iface *iface) + { + struct hostapd_channel_data *channel; +@@ -850,6 +902,13 @@ + if (iface->cac_started) + return hostapd_dfs_start_channel_switch_cac(iface); + ++ /* ++ * Allow selection of DFS channel in ETSI to comply with ++ * uniform spreading. ++ */ ++ if (iface->dfs_domain == HOSTAPD_DFS_REGION_ETSI) ++ skip_radar = 0; ++ + /* Perform channel switch/CSA */ + channel = dfs_get_valid_channel(iface, &secondary_channel, + &vht_oper_centr_freq_seg0_idx, +@@ -868,8 +927,9 @@ + &vht_oper_centr_freq_seg1_idx, + skip_radar); + if (!channel) { +- /* FIXME: Wait for channel(s) to become available */ +- hostapd_disable_iface(iface); ++ wpa_printf(MSG_INFO, ++ "%s: no DFS channels left, waiting for NOP to finish", ++ __func__); + return err; + } + +@@ -992,6 +1052,11 @@ + /* TODO add correct implementation here */ + set_dfs_state(iface, freq, ht_enabled, chan_offset, chan_width, + cf1, cf2, HOSTAPD_CHAN_DFS_USABLE); ++ ++ /* Handle cases where all channels were initially unavailable */ ++ if (iface->state == HAPD_IFACE_DFS && !iface->cac_started) ++ hostapd_handle_dfs(iface); ++ + return 0; + } + +@@ -1059,7 +1124,8 @@ + return 1; + } + +- if (ieee80211_is_dfs(iface->freq)) { ++ if (ieee80211_is_dfs(iface->freq, iface->hw_features, ++ iface->num_hw_features)) { + wpa_printf(MSG_DEBUG, "%s: freq %d MHz requires DFS", + __func__, iface->freq); + return 0; +--- contrib/wpa/src/ap/dfs.h.orig ++++ contrib/wpa/src/ap/dfs.h +@@ -1,7 +1,7 @@ + /* + * DFS - Dynamic Frequency Selection + * Copyright (c) 2002-2013, Jouni Malinen +- * Copyright (c) 2013, Qualcomm Atheros, Inc. ++ * Copyright (c) 2013-2017, Qualcomm Atheros, Inc. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. +@@ -14,6 +14,9 @@ + int hostapd_dfs_complete_cac(struct hostapd_iface *iface, int success, int freq, + int ht_enabled, int chan_offset, int chan_width, + int cf1, int cf2); ++int hostapd_dfs_pre_cac_expired(struct hostapd_iface *iface, int freq, ++ int ht_enabled, int chan_offset, int chan_width, ++ int cf1, int cf2); + int hostapd_dfs_radar_detected(struct hostapd_iface *iface, int freq, + int ht_enabled, + int chan_offset, int chan_width, +--- contrib/wpa/src/ap/dhcp_snoop.c.orig ++++ contrib/wpa/src/ap/dhcp_snoop.c +@@ -7,10 +7,9 @@ + */ + + #include "utils/includes.h" +-#include +-#include + + #include "utils/common.h" ++#include "common/dhcp.h" + #include "l2_packet/l2_packet.h" + #include "hostapd.h" + #include "sta_info.h" +@@ -18,30 +17,7 @@ + #include "x_snoop.h" + #include "dhcp_snoop.h" + +-struct bootp_pkt { +- struct iphdr iph; +- struct udphdr udph; +- u8 op; +- u8 htype; +- u8 hlen; +- u8 hops; +- be32 xid; +- be16 secs; +- be16 flags; +- be32 client_ip; +- be32 your_ip; +- be32 server_ip; +- be32 relay_ip; +- u8 hw_addr[16]; +- u8 serv_name[64]; +- u8 boot_file[128]; +- u8 exten[312]; +-} STRUCT_PACKED; + +-#define DHCPACK 5 +-static const u8 ic_bootp_cookie[] = { 99, 130, 83, 99 }; +- +- + static const char * ipaddr_str(u32 addr) + { + static char buf[17]; +@@ -74,24 +50,26 @@ + if (tot_len > (unsigned int) (len - ETH_HLEN)) + return; + +- if (os_memcmp(b->exten, ic_bootp_cookie, ARRAY_SIZE(ic_bootp_cookie))) ++ if (WPA_GET_BE32(b->exten) != DHCP_MAGIC) + return; + + /* Parse DHCP options */ + end = (const u8 *) b + tot_len; + pos = &b->exten[4]; +- while (pos < end && *pos != 0xff) { ++ while (pos < end && *pos != DHCP_OPT_END) { + const u8 *opt = pos++; + +- if (*opt == 0) /* padding */ ++ if (*opt == DHCP_OPT_PAD) + continue; + ++ if (pos >= end || 1 + *pos > end - pos) ++ break; + pos += *pos + 1; + if (pos >= end) + break; + + switch (*opt) { +- case 1: /* subnet mask */ ++ case DHCP_OPT_SUBNET_MASK: + if (opt[1] == 4) + subnet_mask = WPA_GET_BE32(&opt[2]); + if (subnet_mask == 0) +@@ -101,7 +79,7 @@ + prefixlen--; + } + break; +- case 53: /* message type */ ++ case DHCP_OPT_MSG_TYPE: + if (opt[1]) + msgtype = opt[2]; + break; +@@ -110,6 +88,15 @@ + } + } + ++ if (hapd->conf->disable_dgaf && is_broadcast_ether_addr(buf)) { ++ for (sta = hapd->sta_list; sta; sta = sta->next) { ++ if (!(sta->flags & WLAN_STA_AUTHORIZED)) ++ continue; ++ x_snoop_mcast_to_ucast_convert_send(hapd, sta, ++ (u8 *) buf, len); ++ } ++ } ++ + if (msgtype == DHCPACK) { + if (b->your_ip == 0) + return; +@@ -121,7 +108,8 @@ + + wpa_printf(MSG_DEBUG, "dhcp_snoop: Found DHCPACK for " MACSTR + " @ IPv4 address %s/%d", +- MAC2STR(sta->addr), ipaddr_str(ntohl(b->your_ip)), ++ MAC2STR(sta->addr), ++ ipaddr_str(be_to_host32(b->your_ip)), + prefixlen); + + if (sta->ipaddr == b->your_ip) +@@ -145,15 +133,6 @@ + } + sta->ipaddr = b->your_ip; + } +- +- if (hapd->conf->disable_dgaf && is_broadcast_ether_addr(buf)) { +- for (sta = hapd->sta_list; sta; sta = sta->next) { +- if (!(sta->flags & WLAN_STA_AUTHORIZED)) +- continue; +- x_snoop_mcast_to_ucast_convert_send(hapd, sta, +- (u8 *) buf, len); +- } +- } + } + + +@@ -175,4 +154,5 @@ + void dhcp_snoop_deinit(struct hostapd_data *hapd) + { + l2_packet_deinit(hapd->sock_dhcp); ++ hapd->sock_dhcp = NULL; + } +--- contrib/wpa/src/ap/dpp_hostapd.c.orig ++++ contrib/wpa/src/ap/dpp_hostapd.c +@@ -0,0 +1,1646 @@ ++/* ++ * hostapd / DPP integration ++ * Copyright (c) 2017, 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 "utils/eloop.h" ++#include "common/dpp.h" ++#include "common/gas.h" ++#include "common/wpa_ctrl.h" ++#include "hostapd.h" ++#include "ap_drv_ops.h" ++#include "gas_query_ap.h" ++#include "wpa_auth.h" ++#include "dpp_hostapd.h" ++ ++ ++static void hostapd_dpp_reply_wait_timeout(void *eloop_ctx, void *timeout_ctx); ++static void hostapd_dpp_auth_success(struct hostapd_data *hapd, int initiator); ++static void hostapd_dpp_init_timeout(void *eloop_ctx, void *timeout_ctx); ++static int hostapd_dpp_auth_init_next(struct hostapd_data *hapd); ++ ++static const u8 broadcast[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; ++ ++ ++/** ++ * hostapd_dpp_qr_code - Parse and add DPP bootstrapping info from a QR Code ++ * @hapd: Pointer to hostapd_data ++ * @cmd: DPP URI read from a QR Code ++ * Returns: Identifier of the stored info or -1 on failure ++ */ ++int hostapd_dpp_qr_code(struct hostapd_data *hapd, const char *cmd) ++{ ++ struct dpp_bootstrap_info *bi; ++ struct dpp_authentication *auth = hapd->dpp_auth; ++ ++ bi = dpp_add_qr_code(hapd->iface->interfaces->dpp, cmd); ++ if (!bi) ++ return -1; ++ ++ if (auth && auth->response_pending && ++ dpp_notify_new_qr_code(auth, bi) == 1) { ++ wpa_printf(MSG_DEBUG, ++ "DPP: Sending out pending authentication response"); ++ wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_TX "dst=" MACSTR ++ " freq=%u type=%d", ++ MAC2STR(auth->peer_mac_addr), auth->curr_freq, ++ DPP_PA_AUTHENTICATION_RESP); ++ hostapd_drv_send_action(hapd, auth->curr_freq, 0, ++ auth->peer_mac_addr, ++ wpabuf_head(hapd->dpp_auth->resp_msg), ++ wpabuf_len(hapd->dpp_auth->resp_msg)); ++ } ++ ++ return bi->id; ++} ++ ++ ++static void hostapd_dpp_auth_resp_retry_timeout(void *eloop_ctx, ++ void *timeout_ctx) ++{ ++ struct hostapd_data *hapd = eloop_ctx; ++ struct dpp_authentication *auth = hapd->dpp_auth; ++ ++ if (!auth || !auth->resp_msg) ++ return; ++ ++ wpa_printf(MSG_DEBUG, ++ "DPP: Retry Authentication Response after timeout"); ++ wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_TX "dst=" MACSTR ++ " freq=%u type=%d", ++ MAC2STR(auth->peer_mac_addr), auth->curr_freq, ++ DPP_PA_AUTHENTICATION_RESP); ++ hostapd_drv_send_action(hapd, auth->curr_freq, 500, auth->peer_mac_addr, ++ wpabuf_head(auth->resp_msg), ++ wpabuf_len(auth->resp_msg)); ++} ++ ++ ++static void hostapd_dpp_auth_resp_retry(struct hostapd_data *hapd) ++{ ++ struct dpp_authentication *auth = hapd->dpp_auth; ++ unsigned int wait_time, max_tries; ++ ++ if (!auth || !auth->resp_msg) ++ return; ++ ++ if (hapd->dpp_resp_max_tries) ++ max_tries = hapd->dpp_resp_max_tries; ++ else ++ max_tries = 5; ++ auth->auth_resp_tries++; ++ if (auth->auth_resp_tries >= max_tries) { ++ wpa_printf(MSG_INFO, ++ "DPP: No confirm received from initiator - stopping exchange"); ++ hostapd_drv_send_action_cancel_wait(hapd); ++ dpp_auth_deinit(hapd->dpp_auth); ++ hapd->dpp_auth = NULL; ++ return; ++ } ++ ++ if (hapd->dpp_resp_retry_time) ++ wait_time = hapd->dpp_resp_retry_time; ++ else ++ wait_time = 1000; ++ wpa_printf(MSG_DEBUG, ++ "DPP: Schedule retransmission of Authentication Response frame in %u ms", ++ wait_time); ++ eloop_cancel_timeout(hostapd_dpp_auth_resp_retry_timeout, hapd, NULL); ++ eloop_register_timeout(wait_time / 1000, ++ (wait_time % 1000) * 1000, ++ hostapd_dpp_auth_resp_retry_timeout, hapd, NULL); ++} ++ ++ ++void hostapd_dpp_tx_status(struct hostapd_data *hapd, const u8 *dst, ++ const u8 *data, size_t data_len, int ok) ++{ ++ struct dpp_authentication *auth = hapd->dpp_auth; ++ ++ wpa_printf(MSG_DEBUG, "DPP: TX status: dst=" MACSTR " ok=%d", ++ MAC2STR(dst), ok); ++ wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_TX_STATUS "dst=" MACSTR ++ " result=%s", MAC2STR(dst), ok ? "SUCCESS" : "FAILED"); ++ ++ if (!hapd->dpp_auth) { ++ wpa_printf(MSG_DEBUG, ++ "DPP: Ignore TX status since there is no ongoing authentication exchange"); ++ return; ++ } ++ ++#ifdef CONFIG_DPP2 ++ if (auth->connect_on_tx_status) { ++ wpa_printf(MSG_DEBUG, ++ "DPP: Complete exchange on configuration result"); ++ dpp_auth_deinit(hapd->dpp_auth); ++ hapd->dpp_auth = NULL; ++ return; ++ } ++#endif /* CONFIG_DPP2 */ ++ ++ if (hapd->dpp_auth->remove_on_tx_status) { ++ wpa_printf(MSG_DEBUG, ++ "DPP: Terminate authentication exchange due to an earlier error"); ++ eloop_cancel_timeout(hostapd_dpp_init_timeout, hapd, NULL); ++ eloop_cancel_timeout(hostapd_dpp_reply_wait_timeout, ++ hapd, NULL); ++ eloop_cancel_timeout(hostapd_dpp_auth_resp_retry_timeout, hapd, ++ NULL); ++ hostapd_drv_send_action_cancel_wait(hapd); ++ dpp_auth_deinit(hapd->dpp_auth); ++ hapd->dpp_auth = NULL; ++ return; ++ } ++ ++ if (hapd->dpp_auth_ok_on_ack) ++ hostapd_dpp_auth_success(hapd, 1); ++ ++ if (!is_broadcast_ether_addr(dst) && !ok) { ++ wpa_printf(MSG_DEBUG, ++ "DPP: Unicast DPP Action frame was not ACKed"); ++ if (auth->waiting_auth_resp) { ++ /* In case of DPP Authentication Request frame, move to ++ * the next channel immediately. */ ++ hostapd_drv_send_action_cancel_wait(hapd); ++ hostapd_dpp_auth_init_next(hapd); ++ return; ++ } ++ if (auth->waiting_auth_conf) { ++ hostapd_dpp_auth_resp_retry(hapd); ++ return; ++ } ++ } ++ ++ if (!is_broadcast_ether_addr(dst) && auth->waiting_auth_resp && ok) { ++ /* Allow timeout handling to stop iteration if no response is ++ * received from a peer that has ACKed a request. */ ++ auth->auth_req_ack = 1; ++ } ++ ++ if (!hapd->dpp_auth_ok_on_ack && hapd->dpp_auth->neg_freq > 0 && ++ hapd->dpp_auth->curr_freq != hapd->dpp_auth->neg_freq) { ++ wpa_printf(MSG_DEBUG, ++ "DPP: Move from curr_freq %u MHz to neg_freq %u MHz for response", ++ hapd->dpp_auth->curr_freq, ++ hapd->dpp_auth->neg_freq); ++ hostapd_drv_send_action_cancel_wait(hapd); ++ ++ if (hapd->dpp_auth->neg_freq != ++ (unsigned int) hapd->iface->freq && hapd->iface->freq > 0) { ++ /* TODO: Listen operation on non-operating channel */ ++ wpa_printf(MSG_INFO, ++ "DPP: Listen operation on non-operating channel (%d MHz) is not yet supported (operating channel: %d MHz)", ++ hapd->dpp_auth->neg_freq, hapd->iface->freq); ++ } ++ } ++ ++ if (hapd->dpp_auth_ok_on_ack) ++ hapd->dpp_auth_ok_on_ack = 0; ++} ++ ++ ++static void hostapd_dpp_reply_wait_timeout(void *eloop_ctx, void *timeout_ctx) ++{ ++ struct hostapd_data *hapd = eloop_ctx; ++ struct dpp_authentication *auth = hapd->dpp_auth; ++ unsigned int freq; ++ struct os_reltime now, diff; ++ unsigned int wait_time, diff_ms; ++ ++ if (!auth || !auth->waiting_auth_resp) ++ return; ++ ++ wait_time = hapd->dpp_resp_wait_time ? ++ hapd->dpp_resp_wait_time : 2000; ++ os_get_reltime(&now); ++ os_reltime_sub(&now, &hapd->dpp_last_init, &diff); ++ diff_ms = diff.sec * 1000 + diff.usec / 1000; ++ wpa_printf(MSG_DEBUG, ++ "DPP: Reply wait timeout - wait_time=%u diff_ms=%u", ++ wait_time, diff_ms); ++ ++ if (auth->auth_req_ack && diff_ms >= wait_time) { ++ /* Peer ACK'ed Authentication Request frame, but did not reply ++ * with Authentication Response frame within two seconds. */ ++ wpa_printf(MSG_INFO, ++ "DPP: No response received from responder - stopping initiation attempt"); ++ wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_AUTH_INIT_FAILED); ++ hostapd_drv_send_action_cancel_wait(hapd); ++ hostapd_dpp_listen_stop(hapd); ++ dpp_auth_deinit(auth); ++ hapd->dpp_auth = NULL; ++ return; ++ } ++ ++ if (diff_ms >= wait_time) { ++ /* Authentication Request frame was not ACK'ed and no reply ++ * was receiving within two seconds. */ ++ wpa_printf(MSG_DEBUG, ++ "DPP: Continue Initiator channel iteration"); ++ hostapd_drv_send_action_cancel_wait(hapd); ++ hostapd_dpp_listen_stop(hapd); ++ hostapd_dpp_auth_init_next(hapd); ++ return; ++ } ++ ++ /* Driver did not support 2000 ms long wait_time with TX command, so ++ * schedule listen operation to continue waiting for the response. ++ * ++ * DPP listen operations continue until stopped, so simply schedule a ++ * new call to this function at the point when the two second reply ++ * wait has expired. */ ++ wait_time -= diff_ms; ++ ++ freq = auth->curr_freq; ++ if (auth->neg_freq > 0) ++ freq = auth->neg_freq; ++ wpa_printf(MSG_DEBUG, ++ "DPP: Continue reply wait on channel %u MHz for %u ms", ++ freq, wait_time); ++ hapd->dpp_in_response_listen = 1; ++ ++ if (freq != (unsigned int) hapd->iface->freq && hapd->iface->freq > 0) { ++ /* TODO: Listen operation on non-operating channel */ ++ wpa_printf(MSG_INFO, ++ "DPP: Listen operation on non-operating channel (%d MHz) is not yet supported (operating channel: %d MHz)", ++ freq, hapd->iface->freq); ++ } ++ ++ eloop_register_timeout(wait_time / 1000, (wait_time % 1000) * 1000, ++ hostapd_dpp_reply_wait_timeout, hapd, NULL); ++} ++ ++ ++static void hostapd_dpp_set_testing_options(struct hostapd_data *hapd, ++ struct dpp_authentication *auth) ++{ ++#ifdef CONFIG_TESTING_OPTIONS ++ if (hapd->dpp_config_obj_override) ++ auth->config_obj_override = ++ os_strdup(hapd->dpp_config_obj_override); ++ if (hapd->dpp_discovery_override) ++ auth->discovery_override = ++ os_strdup(hapd->dpp_discovery_override); ++ if (hapd->dpp_groups_override) ++ auth->groups_override = os_strdup(hapd->dpp_groups_override); ++ auth->ignore_netaccesskey_mismatch = ++ hapd->dpp_ignore_netaccesskey_mismatch; ++#endif /* CONFIG_TESTING_OPTIONS */ ++} ++ ++ ++static void hostapd_dpp_init_timeout(void *eloop_ctx, void *timeout_ctx) ++{ ++ struct hostapd_data *hapd = eloop_ctx; ++ ++ if (!hapd->dpp_auth) ++ return; ++ wpa_printf(MSG_DEBUG, "DPP: Retry initiation after timeout"); ++ hostapd_dpp_auth_init_next(hapd); ++} ++ ++ ++static int hostapd_dpp_auth_init_next(struct hostapd_data *hapd) ++{ ++ struct dpp_authentication *auth = hapd->dpp_auth; ++ const u8 *dst; ++ unsigned int wait_time, max_wait_time, freq, max_tries, used; ++ struct os_reltime now, diff; ++ ++ if (!auth) ++ return -1; ++ ++ if (auth->freq_idx == 0) ++ os_get_reltime(&hapd->dpp_init_iter_start); ++ ++ if (auth->freq_idx >= auth->num_freq) { ++ auth->num_freq_iters++; ++ if (hapd->dpp_init_max_tries) ++ max_tries = hapd->dpp_init_max_tries; ++ else ++ max_tries = 5; ++ if (auth->num_freq_iters >= max_tries || auth->auth_req_ack) { ++ wpa_printf(MSG_INFO, ++ "DPP: No response received from responder - stopping initiation attempt"); ++ wpa_msg(hapd->msg_ctx, MSG_INFO, ++ DPP_EVENT_AUTH_INIT_FAILED); ++ eloop_cancel_timeout(hostapd_dpp_reply_wait_timeout, ++ hapd, NULL); ++ hostapd_drv_send_action_cancel_wait(hapd); ++ dpp_auth_deinit(hapd->dpp_auth); ++ hapd->dpp_auth = NULL; ++ return -1; ++ } ++ auth->freq_idx = 0; ++ eloop_cancel_timeout(hostapd_dpp_init_timeout, hapd, NULL); ++ if (hapd->dpp_init_retry_time) ++ wait_time = hapd->dpp_init_retry_time; ++ else ++ wait_time = 10000; ++ os_get_reltime(&now); ++ os_reltime_sub(&now, &hapd->dpp_init_iter_start, &diff); ++ used = diff.sec * 1000 + diff.usec / 1000; ++ if (used > wait_time) ++ wait_time = 0; ++ else ++ wait_time -= used; ++ wpa_printf(MSG_DEBUG, "DPP: Next init attempt in %u ms", ++ wait_time); ++ eloop_register_timeout(wait_time / 1000, ++ (wait_time % 1000) * 1000, ++ hostapd_dpp_init_timeout, hapd, ++ NULL); ++ return 0; ++ } ++ freq = auth->freq[auth->freq_idx++]; ++ auth->curr_freq = freq; ++ ++ if (is_zero_ether_addr(auth->peer_bi->mac_addr)) ++ dst = broadcast; ++ else ++ dst = auth->peer_bi->mac_addr; ++ hapd->dpp_auth_ok_on_ack = 0; ++ eloop_cancel_timeout(hostapd_dpp_reply_wait_timeout, hapd, NULL); ++ wait_time = 2000; /* TODO: hapd->max_remain_on_chan; */ ++ max_wait_time = hapd->dpp_resp_wait_time ? ++ hapd->dpp_resp_wait_time : 2000; ++ if (wait_time > max_wait_time) ++ wait_time = max_wait_time; ++ wait_time += 10; /* give the driver some extra time to complete */ ++ eloop_register_timeout(wait_time / 1000, (wait_time % 1000) * 1000, ++ hostapd_dpp_reply_wait_timeout, hapd, NULL); ++ wait_time -= 10; ++ if (auth->neg_freq > 0 && freq != auth->neg_freq) { ++ wpa_printf(MSG_DEBUG, ++ "DPP: Initiate on %u MHz and move to neg_freq %u MHz for response", ++ freq, auth->neg_freq); ++ } ++ wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_TX "dst=" MACSTR ++ " freq=%u type=%d", ++ MAC2STR(dst), freq, DPP_PA_AUTHENTICATION_REQ); ++ auth->auth_req_ack = 0; ++ os_get_reltime(&hapd->dpp_last_init); ++ return hostapd_drv_send_action(hapd, freq, wait_time, ++ dst, ++ wpabuf_head(hapd->dpp_auth->req_msg), ++ wpabuf_len(hapd->dpp_auth->req_msg)); ++} ++ ++ ++int hostapd_dpp_auth_init(struct hostapd_data *hapd, const char *cmd) ++{ ++ const char *pos; ++ struct dpp_bootstrap_info *peer_bi, *own_bi = NULL; ++ u8 allowed_roles = DPP_CAPAB_CONFIGURATOR; ++ unsigned int neg_freq = 0; ++ ++ pos = os_strstr(cmd, " peer="); ++ if (!pos) ++ return -1; ++ pos += 6; ++ peer_bi = dpp_bootstrap_get_id(hapd->iface->interfaces->dpp, atoi(pos)); ++ if (!peer_bi) { ++ wpa_printf(MSG_INFO, ++ "DPP: Could not find bootstrapping info for the identified peer"); ++ return -1; ++ } ++ ++ pos = os_strstr(cmd, " own="); ++ if (pos) { ++ pos += 5; ++ own_bi = dpp_bootstrap_get_id(hapd->iface->interfaces->dpp, ++ atoi(pos)); ++ if (!own_bi) { ++ wpa_printf(MSG_INFO, ++ "DPP: Could not find bootstrapping info for the identified local entry"); ++ return -1; ++ } ++ ++ if (peer_bi->curve != own_bi->curve) { ++ wpa_printf(MSG_INFO, ++ "DPP: Mismatching curves in bootstrapping info (peer=%s own=%s)", ++ peer_bi->curve->name, own_bi->curve->name); ++ return -1; ++ } ++ } ++ ++ pos = os_strstr(cmd, " role="); ++ if (pos) { ++ pos += 6; ++ if (os_strncmp(pos, "configurator", 12) == 0) ++ allowed_roles = DPP_CAPAB_CONFIGURATOR; ++ else if (os_strncmp(pos, "enrollee", 8) == 0) ++ allowed_roles = DPP_CAPAB_ENROLLEE; ++ else if (os_strncmp(pos, "either", 6) == 0) ++ allowed_roles = DPP_CAPAB_CONFIGURATOR | ++ DPP_CAPAB_ENROLLEE; ++ else ++ goto fail; ++ } ++ ++ pos = os_strstr(cmd, " neg_freq="); ++ if (pos) ++ neg_freq = atoi(pos + 10); ++ ++ if (hapd->dpp_auth) { ++ eloop_cancel_timeout(hostapd_dpp_init_timeout, hapd, NULL); ++ eloop_cancel_timeout(hostapd_dpp_reply_wait_timeout, ++ hapd, NULL); ++ eloop_cancel_timeout(hostapd_dpp_auth_resp_retry_timeout, hapd, ++ NULL); ++ hostapd_drv_send_action_cancel_wait(hapd); ++ dpp_auth_deinit(hapd->dpp_auth); ++ } ++ ++ hapd->dpp_auth = dpp_auth_init(hapd->msg_ctx, peer_bi, own_bi, ++ allowed_roles, neg_freq, ++ hapd->iface->hw_features, ++ hapd->iface->num_hw_features); ++ if (!hapd->dpp_auth) ++ goto fail; ++ hostapd_dpp_set_testing_options(hapd, hapd->dpp_auth); ++ if (dpp_set_configurator(hapd->iface->interfaces->dpp, hapd->msg_ctx, ++ hapd->dpp_auth, cmd) < 0) { ++ dpp_auth_deinit(hapd->dpp_auth); ++ hapd->dpp_auth = NULL; ++ goto fail; ++ } ++ ++ hapd->dpp_auth->neg_freq = neg_freq; ++ ++ if (!is_zero_ether_addr(peer_bi->mac_addr)) ++ os_memcpy(hapd->dpp_auth->peer_mac_addr, peer_bi->mac_addr, ++ ETH_ALEN); ++ ++ return hostapd_dpp_auth_init_next(hapd); ++fail: ++ return -1; ++} ++ ++ ++int hostapd_dpp_listen(struct hostapd_data *hapd, const char *cmd) ++{ ++ int freq; ++ ++ freq = atoi(cmd); ++ if (freq <= 0) ++ return -1; ++ ++ if (os_strstr(cmd, " role=configurator")) ++ hapd->dpp_allowed_roles = DPP_CAPAB_CONFIGURATOR; ++ else if (os_strstr(cmd, " role=enrollee")) ++ hapd->dpp_allowed_roles = DPP_CAPAB_ENROLLEE; ++ else ++ hapd->dpp_allowed_roles = DPP_CAPAB_CONFIGURATOR | ++ DPP_CAPAB_ENROLLEE; ++ hapd->dpp_qr_mutual = os_strstr(cmd, " qr=mutual") != NULL; ++ ++ if (freq != hapd->iface->freq && hapd->iface->freq > 0) { ++ /* TODO: Listen operation on non-operating channel */ ++ wpa_printf(MSG_INFO, ++ "DPP: Listen operation on non-operating channel (%d MHz) is not yet supported (operating channel: %d MHz)", ++ freq, hapd->iface->freq); ++ return -1; ++ } ++ ++ return 0; ++} ++ ++ ++void hostapd_dpp_listen_stop(struct hostapd_data *hapd) ++{ ++ /* TODO: Stop listen operation on non-operating channel */ ++} ++ ++ ++static void hostapd_dpp_rx_auth_req(struct hostapd_data *hapd, const u8 *src, ++ const u8 *hdr, const u8 *buf, size_t len, ++ unsigned int freq) ++{ ++ const u8 *r_bootstrap, *i_bootstrap; ++ u16 r_bootstrap_len, i_bootstrap_len; ++ struct dpp_bootstrap_info *own_bi = NULL, *peer_bi = NULL; ++ ++ if (!hapd->iface->interfaces->dpp) ++ return; ++ ++ wpa_printf(MSG_DEBUG, "DPP: Authentication Request from " MACSTR, ++ MAC2STR(src)); ++ ++ r_bootstrap = dpp_get_attr(buf, len, DPP_ATTR_R_BOOTSTRAP_KEY_HASH, ++ &r_bootstrap_len); ++ if (!r_bootstrap || r_bootstrap_len != SHA256_MAC_LEN) { ++ wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_FAIL ++ "Missing or invalid required Responder Bootstrapping Key Hash attribute"); ++ return; ++ } ++ wpa_hexdump(MSG_MSGDUMP, "DPP: Responder Bootstrapping Key Hash", ++ r_bootstrap, r_bootstrap_len); ++ ++ i_bootstrap = dpp_get_attr(buf, len, DPP_ATTR_I_BOOTSTRAP_KEY_HASH, ++ &i_bootstrap_len); ++ if (!i_bootstrap || i_bootstrap_len != SHA256_MAC_LEN) { ++ wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_FAIL ++ "Missing or invalid required Initiator Bootstrapping Key Hash attribute"); ++ return; ++ } ++ wpa_hexdump(MSG_MSGDUMP, "DPP: Initiator Bootstrapping Key Hash", ++ i_bootstrap, i_bootstrap_len); ++ ++ /* Try to find own and peer bootstrapping key matches based on the ++ * received hash values */ ++ dpp_bootstrap_find_pair(hapd->iface->interfaces->dpp, i_bootstrap, ++ r_bootstrap, &own_bi, &peer_bi); ++ if (!own_bi) { ++ wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_FAIL ++ "No matching own bootstrapping key found - ignore message"); ++ return; ++ } ++ ++ if (hapd->dpp_auth) { ++ wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_FAIL ++ "Already in DPP authentication exchange - ignore new one"); ++ return; ++ } ++ ++ hapd->dpp_auth_ok_on_ack = 0; ++ hapd->dpp_auth = dpp_auth_req_rx(hapd->msg_ctx, hapd->dpp_allowed_roles, ++ hapd->dpp_qr_mutual, ++ peer_bi, own_bi, freq, hdr, buf, len); ++ if (!hapd->dpp_auth) { ++ wpa_printf(MSG_DEBUG, "DPP: No response generated"); ++ return; ++ } ++ hostapd_dpp_set_testing_options(hapd, hapd->dpp_auth); ++ if (dpp_set_configurator(hapd->iface->interfaces->dpp, hapd->msg_ctx, ++ hapd->dpp_auth, ++ hapd->dpp_configurator_params) < 0) { ++ dpp_auth_deinit(hapd->dpp_auth); ++ hapd->dpp_auth = NULL; ++ return; ++ } ++ os_memcpy(hapd->dpp_auth->peer_mac_addr, src, ETH_ALEN); ++ ++ wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_TX "dst=" MACSTR ++ " freq=%u type=%d", ++ MAC2STR(src), hapd->dpp_auth->curr_freq, ++ DPP_PA_AUTHENTICATION_RESP); ++ hostapd_drv_send_action(hapd, hapd->dpp_auth->curr_freq, 0, ++ src, wpabuf_head(hapd->dpp_auth->resp_msg), ++ wpabuf_len(hapd->dpp_auth->resp_msg)); ++} ++ ++ ++static void hostapd_dpp_handle_config_obj(struct hostapd_data *hapd, ++ struct dpp_authentication *auth) ++{ ++ wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONF_RECEIVED); ++ wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONFOBJ_AKM "%s", ++ dpp_akm_str(auth->akm)); ++ if (auth->ssid_len) ++ wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONFOBJ_SSID "%s", ++ wpa_ssid_txt(auth->ssid, auth->ssid_len)); ++ if (auth->connector) { ++ /* TODO: Save the Connector and consider using a command ++ * to fetch the value instead of sending an event with ++ * it. The Connector could end up being larger than what ++ * most clients are ready to receive as an event ++ * message. */ ++ wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONNECTOR "%s", ++ auth->connector); ++ } else if (auth->passphrase[0]) { ++ char hex[64 * 2 + 1]; ++ ++ wpa_snprintf_hex(hex, sizeof(hex), ++ (const u8 *) auth->passphrase, ++ os_strlen(auth->passphrase)); ++ wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONFOBJ_PASS "%s", ++ hex); ++ } else if (auth->psk_set) { ++ char hex[PMK_LEN * 2 + 1]; ++ ++ wpa_snprintf_hex(hex, sizeof(hex), auth->psk, PMK_LEN); ++ wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONFOBJ_PSK "%s", ++ hex); ++ } ++ if (auth->c_sign_key) { ++ char *hex; ++ size_t hexlen; ++ ++ hexlen = 2 * wpabuf_len(auth->c_sign_key) + 1; ++ hex = os_malloc(hexlen); ++ if (hex) { ++ wpa_snprintf_hex(hex, hexlen, ++ wpabuf_head(auth->c_sign_key), ++ wpabuf_len(auth->c_sign_key)); ++ wpa_msg(hapd->msg_ctx, MSG_INFO, ++ DPP_EVENT_C_SIGN_KEY "%s", hex); ++ os_free(hex); ++ } ++ } ++ if (auth->net_access_key) { ++ char *hex; ++ size_t hexlen; ++ ++ hexlen = 2 * wpabuf_len(auth->net_access_key) + 1; ++ hex = os_malloc(hexlen); ++ if (hex) { ++ wpa_snprintf_hex(hex, hexlen, ++ wpabuf_head(auth->net_access_key), ++ wpabuf_len(auth->net_access_key)); ++ if (auth->net_access_key_expiry) ++ wpa_msg(hapd->msg_ctx, MSG_INFO, ++ DPP_EVENT_NET_ACCESS_KEY "%s %lu", hex, ++ (unsigned long) ++ auth->net_access_key_expiry); ++ else ++ wpa_msg(hapd->msg_ctx, MSG_INFO, ++ DPP_EVENT_NET_ACCESS_KEY "%s", hex); ++ os_free(hex); ++ } ++ } ++} ++ ++ ++static void hostapd_dpp_gas_resp_cb(void *ctx, const u8 *addr, u8 dialog_token, ++ enum gas_query_ap_result result, ++ const struct wpabuf *adv_proto, ++ const struct wpabuf *resp, u16 status_code) ++{ ++ struct hostapd_data *hapd = ctx; ++ const u8 *pos; ++ struct dpp_authentication *auth = hapd->dpp_auth; ++ enum dpp_status_error status = DPP_STATUS_CONFIG_REJECTED; ++ ++ if (!auth || !auth->auth_success) { ++ wpa_printf(MSG_DEBUG, "DPP: No matching exchange in progress"); ++ return; ++ } ++ if (!resp || status_code != WLAN_STATUS_SUCCESS) { ++ wpa_printf(MSG_DEBUG, "DPP: GAS query did not succeed"); ++ goto fail; ++ } ++ ++ wpa_hexdump_buf(MSG_DEBUG, "DPP: Configuration Response adv_proto", ++ adv_proto); ++ wpa_hexdump_buf(MSG_DEBUG, "DPP: Configuration Response (GAS response)", ++ resp); ++ ++ if (wpabuf_len(adv_proto) != 10 || ++ !(pos = wpabuf_head(adv_proto)) || ++ pos[0] != WLAN_EID_ADV_PROTO || ++ pos[1] != 8 || ++ pos[3] != WLAN_EID_VENDOR_SPECIFIC || ++ pos[4] != 5 || ++ WPA_GET_BE24(&pos[5]) != OUI_WFA || ++ pos[8] != 0x1a || ++ pos[9] != 1) { ++ wpa_printf(MSG_DEBUG, ++ "DPP: Not a DPP Advertisement Protocol ID"); ++ goto fail; ++ } ++ ++ if (dpp_conf_resp_rx(auth, resp) < 0) { ++ wpa_printf(MSG_DEBUG, "DPP: Configuration attempt failed"); ++ goto fail; ++ } ++ ++ hostapd_dpp_handle_config_obj(hapd, auth); ++ status = DPP_STATUS_OK; ++#ifdef CONFIG_TESTING_OPTIONS ++ if (dpp_test == DPP_TEST_REJECT_CONFIG) { ++ wpa_printf(MSG_INFO, "DPP: TESTING - Reject Config Object"); ++ status = DPP_STATUS_CONFIG_REJECTED; ++ } ++#endif /* CONFIG_TESTING_OPTIONS */ ++fail: ++ if (status != DPP_STATUS_OK) ++ wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONF_FAILED); ++#ifdef CONFIG_DPP2 ++ if (auth->peer_version >= 2 && ++ auth->conf_resp_status == DPP_STATUS_OK) { ++ struct wpabuf *msg; ++ ++ wpa_printf(MSG_DEBUG, "DPP: Send DPP Configuration Result"); ++ msg = dpp_build_conf_result(auth, status); ++ if (!msg) ++ goto fail2; ++ ++ wpa_msg(hapd->msg_ctx, MSG_INFO, ++ DPP_EVENT_TX "dst=" MACSTR " freq=%u type=%d", ++ MAC2STR(addr), auth->curr_freq, ++ DPP_PA_CONFIGURATION_RESULT); ++ hostapd_drv_send_action(hapd, auth->curr_freq, 0, ++ addr, wpabuf_head(msg), ++ wpabuf_len(msg)); ++ wpabuf_free(msg); ++ ++ /* This exchange will be terminated in the TX status handler */ ++ auth->connect_on_tx_status = 1; ++ return; ++ } ++fail2: ++#endif /* CONFIG_DPP2 */ ++ dpp_auth_deinit(hapd->dpp_auth); ++ hapd->dpp_auth = NULL; ++} ++ ++ ++static void hostapd_dpp_start_gas_client(struct hostapd_data *hapd) ++{ ++ struct dpp_authentication *auth = hapd->dpp_auth; ++ struct wpabuf *buf; ++ char json[100]; ++ int res; ++ int netrole_ap = 1; ++ ++ os_snprintf(json, sizeof(json), ++ "{\"name\":\"Test\"," ++ "\"wi-fi_tech\":\"infra\"," ++ "\"netRole\":\"%s\"}", ++ netrole_ap ? "ap" : "sta"); ++ wpa_printf(MSG_DEBUG, "DPP: GAS Config Attributes: %s", json); ++ ++ buf = dpp_build_conf_req(auth, json); ++ if (!buf) { ++ wpa_printf(MSG_DEBUG, ++ "DPP: No configuration request data available"); ++ return; ++ } ++ ++ wpa_printf(MSG_DEBUG, "DPP: GAS request to " MACSTR " (freq %u MHz)", ++ MAC2STR(auth->peer_mac_addr), auth->curr_freq); ++ ++ res = gas_query_ap_req(hapd->gas, auth->peer_mac_addr, auth->curr_freq, ++ buf, hostapd_dpp_gas_resp_cb, hapd); ++ if (res < 0) { ++ wpa_msg(hapd->msg_ctx, MSG_DEBUG, ++ "GAS: Failed to send Query Request"); ++ wpabuf_free(buf); ++ } else { ++ wpa_printf(MSG_DEBUG, ++ "DPP: GAS query started with dialog token %u", res); ++ } ++} ++ ++ ++static void hostapd_dpp_auth_success(struct hostapd_data *hapd, int initiator) ++{ ++ wpa_printf(MSG_DEBUG, "DPP: Authentication succeeded"); ++ wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_AUTH_SUCCESS "init=%d", ++ initiator); ++#ifdef CONFIG_TESTING_OPTIONS ++ if (dpp_test == DPP_TEST_STOP_AT_AUTH_CONF) { ++ wpa_printf(MSG_INFO, ++ "DPP: TESTING - stop at Authentication Confirm"); ++ if (hapd->dpp_auth->configurator) { ++ /* Prevent GAS response */ ++ hapd->dpp_auth->auth_success = 0; ++ } ++ return; ++ } ++#endif /* CONFIG_TESTING_OPTIONS */ ++ ++ if (!hapd->dpp_auth->configurator) ++ hostapd_dpp_start_gas_client(hapd); ++} ++ ++ ++static void hostapd_dpp_rx_auth_resp(struct hostapd_data *hapd, const u8 *src, ++ const u8 *hdr, const u8 *buf, size_t len, ++ unsigned int freq) ++{ ++ struct dpp_authentication *auth = hapd->dpp_auth; ++ struct wpabuf *msg; ++ ++ wpa_printf(MSG_DEBUG, "DPP: Authentication Response from " MACSTR, ++ MAC2STR(src)); ++ ++ if (!auth) { ++ wpa_printf(MSG_DEBUG, ++ "DPP: No DPP Authentication in progress - drop"); ++ return; ++ } ++ ++ if (!is_zero_ether_addr(auth->peer_mac_addr) && ++ os_memcmp(src, auth->peer_mac_addr, ETH_ALEN) != 0) { ++ wpa_printf(MSG_DEBUG, "DPP: MAC address mismatch (expected " ++ MACSTR ") - drop", MAC2STR(auth->peer_mac_addr)); ++ return; ++ } ++ ++ eloop_cancel_timeout(hostapd_dpp_reply_wait_timeout, hapd, NULL); ++ ++ if (auth->curr_freq != freq && auth->neg_freq == freq) { ++ wpa_printf(MSG_DEBUG, ++ "DPP: Responder accepted request for different negotiation channel"); ++ auth->curr_freq = freq; ++ } ++ ++ eloop_cancel_timeout(hostapd_dpp_init_timeout, hapd, NULL); ++ msg = dpp_auth_resp_rx(auth, hdr, buf, len); ++ if (!msg) { ++ if (auth->auth_resp_status == DPP_STATUS_RESPONSE_PENDING) { ++ wpa_printf(MSG_DEBUG, "DPP: Wait for full response"); ++ return; ++ } ++ wpa_printf(MSG_DEBUG, "DPP: No confirm generated"); ++ return; ++ } ++ os_memcpy(auth->peer_mac_addr, src, ETH_ALEN); ++ ++ wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_TX "dst=" MACSTR ++ " freq=%u type=%d", MAC2STR(src), auth->curr_freq, ++ DPP_PA_AUTHENTICATION_CONF); ++ hostapd_drv_send_action(hapd, auth->curr_freq, 0, src, ++ wpabuf_head(msg), wpabuf_len(msg)); ++ wpabuf_free(msg); ++ hapd->dpp_auth_ok_on_ack = 1; ++} ++ ++ ++static void hostapd_dpp_rx_auth_conf(struct hostapd_data *hapd, const u8 *src, ++ const u8 *hdr, const u8 *buf, size_t len) ++{ ++ struct dpp_authentication *auth = hapd->dpp_auth; ++ ++ wpa_printf(MSG_DEBUG, "DPP: Authentication Confirmation from " MACSTR, ++ MAC2STR(src)); ++ ++ if (!auth) { ++ wpa_printf(MSG_DEBUG, ++ "DPP: No DPP Authentication in progress - drop"); ++ return; ++ } ++ ++ if (os_memcmp(src, auth->peer_mac_addr, ETH_ALEN) != 0) { ++ wpa_printf(MSG_DEBUG, "DPP: MAC address mismatch (expected " ++ MACSTR ") - drop", MAC2STR(auth->peer_mac_addr)); ++ return; ++ } ++ ++ if (dpp_auth_conf_rx(auth, hdr, buf, len) < 0) { ++ wpa_printf(MSG_DEBUG, "DPP: Authentication failed"); ++ return; ++ } ++ ++ hostapd_dpp_auth_success(hapd, 0); ++} ++ ++ ++#ifdef CONFIG_DPP2 ++ ++static void hostapd_dpp_config_result_wait_timeout(void *eloop_ctx, ++ void *timeout_ctx) ++{ ++ struct hostapd_data *hapd = eloop_ctx; ++ struct dpp_authentication *auth = hapd->dpp_auth; ++ ++ if (!auth || !auth->waiting_conf_result) ++ return; ++ ++ wpa_printf(MSG_DEBUG, ++ "DPP: Timeout while waiting for Configuration Result"); ++ wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONF_FAILED); ++ dpp_auth_deinit(auth); ++ hapd->dpp_auth = NULL; ++} ++ ++ ++static void hostapd_dpp_rx_conf_result(struct hostapd_data *hapd, const u8 *src, ++ const u8 *hdr, const u8 *buf, size_t len) ++{ ++ struct dpp_authentication *auth = hapd->dpp_auth; ++ enum dpp_status_error status; ++ ++ wpa_printf(MSG_DEBUG, "DPP: Configuration Result from " MACSTR, ++ MAC2STR(src)); ++ ++ if (!auth || !auth->waiting_conf_result) { ++ wpa_printf(MSG_DEBUG, ++ "DPP: No DPP Configuration waiting for result - drop"); ++ return; ++ } ++ ++ if (os_memcmp(src, auth->peer_mac_addr, ETH_ALEN) != 0) { ++ wpa_printf(MSG_DEBUG, "DPP: MAC address mismatch (expected " ++ MACSTR ") - drop", MAC2STR(auth->peer_mac_addr)); ++ return; ++ } ++ ++ status = dpp_conf_result_rx(auth, hdr, buf, len); ++ ++ hostapd_drv_send_action_cancel_wait(hapd); ++ hostapd_dpp_listen_stop(hapd); ++ if (status == DPP_STATUS_OK) ++ wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONF_SENT); ++ else ++ wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONF_FAILED); ++ dpp_auth_deinit(auth); ++ hapd->dpp_auth = NULL; ++ eloop_cancel_timeout(hostapd_dpp_config_result_wait_timeout, hapd, ++ NULL); ++} ++ ++#endif /* CONFIG_DPP2 */ ++ ++ ++static void hostapd_dpp_send_peer_disc_resp(struct hostapd_data *hapd, ++ const u8 *src, unsigned int freq, ++ u8 trans_id, ++ enum dpp_status_error status) ++{ ++ struct wpabuf *msg; ++ ++ msg = dpp_alloc_msg(DPP_PA_PEER_DISCOVERY_RESP, ++ 5 + 5 + 4 + os_strlen(hapd->conf->dpp_connector)); ++ if (!msg) ++ return; ++ ++#ifdef CONFIG_TESTING_OPTIONS ++ if (dpp_test == DPP_TEST_NO_TRANSACTION_ID_PEER_DISC_RESP) { ++ wpa_printf(MSG_INFO, "DPP: TESTING - no Transaction ID"); ++ goto skip_trans_id; ++ } ++ if (dpp_test == DPP_TEST_INVALID_TRANSACTION_ID_PEER_DISC_RESP) { ++ wpa_printf(MSG_INFO, "DPP: TESTING - invalid Transaction ID"); ++ trans_id ^= 0x01; ++ } ++#endif /* CONFIG_TESTING_OPTIONS */ ++ ++ /* Transaction ID */ ++ wpabuf_put_le16(msg, DPP_ATTR_TRANSACTION_ID); ++ wpabuf_put_le16(msg, 1); ++ wpabuf_put_u8(msg, trans_id); ++ ++#ifdef CONFIG_TESTING_OPTIONS ++skip_trans_id: ++ if (dpp_test == DPP_TEST_NO_STATUS_PEER_DISC_RESP) { ++ wpa_printf(MSG_INFO, "DPP: TESTING - no Status"); ++ goto skip_status; ++ } ++ if (dpp_test == DPP_TEST_INVALID_STATUS_PEER_DISC_RESP) { ++ wpa_printf(MSG_INFO, "DPP: TESTING - invalid Status"); ++ status = 254; ++ } ++#endif /* CONFIG_TESTING_OPTIONS */ ++ ++ /* DPP Status */ ++ wpabuf_put_le16(msg, DPP_ATTR_STATUS); ++ wpabuf_put_le16(msg, 1); ++ wpabuf_put_u8(msg, status); ++ ++#ifdef CONFIG_TESTING_OPTIONS ++skip_status: ++ if (dpp_test == DPP_TEST_NO_CONNECTOR_PEER_DISC_RESP) { ++ wpa_printf(MSG_INFO, "DPP: TESTING - no Connector"); ++ goto skip_connector; ++ } ++ if (status == DPP_STATUS_OK && ++ dpp_test == DPP_TEST_INVALID_CONNECTOR_PEER_DISC_RESP) { ++ char *connector; ++ ++ wpa_printf(MSG_INFO, "DPP: TESTING - invalid Connector"); ++ connector = dpp_corrupt_connector_signature( ++ hapd->conf->dpp_connector); ++ if (!connector) { ++ wpabuf_free(msg); ++ return; ++ } ++ wpabuf_put_le16(msg, DPP_ATTR_CONNECTOR); ++ wpabuf_put_le16(msg, os_strlen(connector)); ++ wpabuf_put_str(msg, connector); ++ os_free(connector); ++ goto skip_connector; ++ } ++#endif /* CONFIG_TESTING_OPTIONS */ ++ ++ /* DPP Connector */ ++ if (status == DPP_STATUS_OK) { ++ wpabuf_put_le16(msg, DPP_ATTR_CONNECTOR); ++ wpabuf_put_le16(msg, os_strlen(hapd->conf->dpp_connector)); ++ wpabuf_put_str(msg, hapd->conf->dpp_connector); ++ } ++ ++#ifdef CONFIG_TESTING_OPTIONS ++skip_connector: ++#endif /* CONFIG_TESTING_OPTIONS */ ++ ++ wpa_printf(MSG_DEBUG, "DPP: Send Peer Discovery Response to " MACSTR ++ " status=%d", MAC2STR(src), status); ++ wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_TX "dst=" MACSTR ++ " freq=%u type=%d status=%d", MAC2STR(src), freq, ++ DPP_PA_PEER_DISCOVERY_RESP, status); ++ hostapd_drv_send_action(hapd, freq, 0, src, ++ wpabuf_head(msg), wpabuf_len(msg)); ++ wpabuf_free(msg); ++} ++ ++ ++static void hostapd_dpp_rx_peer_disc_req(struct hostapd_data *hapd, ++ const u8 *src, ++ const u8 *buf, size_t len, ++ unsigned int freq) ++{ ++ const u8 *connector, *trans_id; ++ u16 connector_len, trans_id_len; ++ struct os_time now; ++ struct dpp_introduction intro; ++ os_time_t expire; ++ int expiration; ++ enum dpp_status_error res; ++ ++ wpa_printf(MSG_DEBUG, "DPP: Peer Discovery Request from " MACSTR, ++ MAC2STR(src)); ++ if (!hapd->wpa_auth || ++ !(hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_DPP) || ++ !(hapd->conf->wpa & WPA_PROTO_RSN)) { ++ wpa_printf(MSG_DEBUG, "DPP: DPP AKM not in use"); ++ return; ++ } ++ ++ if (!hapd->conf->dpp_connector || !hapd->conf->dpp_netaccesskey || ++ !hapd->conf->dpp_csign) { ++ wpa_printf(MSG_DEBUG, "DPP: No own Connector/keys set"); ++ return; ++ } ++ ++ os_get_time(&now); ++ ++ if (hapd->conf->dpp_netaccesskey_expiry && ++ (os_time_t) hapd->conf->dpp_netaccesskey_expiry < now.sec) { ++ wpa_printf(MSG_INFO, "DPP: Own netAccessKey expired"); ++ return; ++ } ++ ++ trans_id = dpp_get_attr(buf, len, DPP_ATTR_TRANSACTION_ID, ++ &trans_id_len); ++ if (!trans_id || trans_id_len != 1) { ++ wpa_printf(MSG_DEBUG, ++ "DPP: Peer did not include Transaction ID"); ++ return; ++ } ++ ++ connector = dpp_get_attr(buf, len, DPP_ATTR_CONNECTOR, &connector_len); ++ if (!connector) { ++ wpa_printf(MSG_DEBUG, ++ "DPP: Peer did not include its Connector"); ++ return; ++ } ++ ++ res = dpp_peer_intro(&intro, hapd->conf->dpp_connector, ++ wpabuf_head(hapd->conf->dpp_netaccesskey), ++ wpabuf_len(hapd->conf->dpp_netaccesskey), ++ wpabuf_head(hapd->conf->dpp_csign), ++ wpabuf_len(hapd->conf->dpp_csign), ++ connector, connector_len, &expire); ++ if (res == 255) { ++ wpa_printf(MSG_INFO, ++ "DPP: Network Introduction protocol resulted in internal failure (peer " ++ MACSTR ")", MAC2STR(src)); ++ return; ++ } ++ if (res != DPP_STATUS_OK) { ++ wpa_printf(MSG_INFO, ++ "DPP: Network Introduction protocol resulted in failure (peer " ++ MACSTR " status %d)", MAC2STR(src), res); ++ hostapd_dpp_send_peer_disc_resp(hapd, src, freq, trans_id[0], ++ res); ++ return; ++ } ++ ++ if (!expire || (os_time_t) hapd->conf->dpp_netaccesskey_expiry < expire) ++ expire = hapd->conf->dpp_netaccesskey_expiry; ++ if (expire) ++ expiration = expire - now.sec; ++ else ++ expiration = 0; ++ ++ if (wpa_auth_pmksa_add2(hapd->wpa_auth, src, intro.pmk, intro.pmk_len, ++ intro.pmkid, expiration, ++ WPA_KEY_MGMT_DPP) < 0) { ++ wpa_printf(MSG_ERROR, "DPP: Failed to add PMKSA cache entry"); ++ return; ++ } ++ ++ hostapd_dpp_send_peer_disc_resp(hapd, src, freq, trans_id[0], ++ DPP_STATUS_OK); ++} ++ ++ ++static void ++hostapd_dpp_rx_pkex_exchange_req(struct hostapd_data *hapd, const u8 *src, ++ const u8 *buf, size_t len, ++ unsigned int freq) ++{ ++ struct wpabuf *msg; ++ ++ wpa_printf(MSG_DEBUG, "DPP: PKEX Exchange Request from " MACSTR, ++ MAC2STR(src)); ++ ++ /* TODO: Support multiple PKEX codes by iterating over all the enabled ++ * values here */ ++ ++ if (!hapd->dpp_pkex_code || !hapd->dpp_pkex_bi) { ++ wpa_printf(MSG_DEBUG, ++ "DPP: No PKEX code configured - ignore request"); ++ return; ++ } ++ ++ if (hapd->dpp_pkex) { ++ /* TODO: Support parallel operations */ ++ wpa_printf(MSG_DEBUG, ++ "DPP: Already in PKEX session - ignore new request"); ++ return; ++ } ++ ++ hapd->dpp_pkex = dpp_pkex_rx_exchange_req(hapd->msg_ctx, ++ hapd->dpp_pkex_bi, ++ hapd->own_addr, src, ++ hapd->dpp_pkex_identifier, ++ hapd->dpp_pkex_code, ++ buf, len); ++ if (!hapd->dpp_pkex) { ++ wpa_printf(MSG_DEBUG, ++ "DPP: Failed to process the request - ignore it"); ++ return; ++ } ++ ++ msg = hapd->dpp_pkex->exchange_resp; ++ wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_TX "dst=" MACSTR ++ " freq=%u type=%d", MAC2STR(src), freq, ++ DPP_PA_PKEX_EXCHANGE_RESP); ++ hostapd_drv_send_action(hapd, freq, 0, src, ++ wpabuf_head(msg), wpabuf_len(msg)); ++ if (hapd->dpp_pkex->failed) { ++ wpa_printf(MSG_DEBUG, ++ "DPP: Terminate PKEX exchange due to an earlier error"); ++ if (hapd->dpp_pkex->t > hapd->dpp_pkex->own_bi->pkex_t) ++ hapd->dpp_pkex->own_bi->pkex_t = hapd->dpp_pkex->t; ++ dpp_pkex_free(hapd->dpp_pkex); ++ hapd->dpp_pkex = NULL; ++ } ++} ++ ++ ++static void ++hostapd_dpp_rx_pkex_exchange_resp(struct hostapd_data *hapd, const u8 *src, ++ const u8 *buf, size_t len, unsigned int freq) ++{ ++ struct wpabuf *msg; ++ ++ wpa_printf(MSG_DEBUG, "DPP: PKEX Exchange Response from " MACSTR, ++ MAC2STR(src)); ++ ++ /* TODO: Support multiple PKEX codes by iterating over all the enabled ++ * values here */ ++ ++ if (!hapd->dpp_pkex || !hapd->dpp_pkex->initiator || ++ hapd->dpp_pkex->exchange_done) { ++ wpa_printf(MSG_DEBUG, "DPP: No matching PKEX session"); ++ return; ++ } ++ ++ msg = dpp_pkex_rx_exchange_resp(hapd->dpp_pkex, src, buf, len); ++ if (!msg) { ++ wpa_printf(MSG_DEBUG, "DPP: Failed to process the response"); ++ return; ++ } ++ ++ wpa_printf(MSG_DEBUG, "DPP: Send PKEX Commit-Reveal Request to " MACSTR, ++ MAC2STR(src)); ++ ++ wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_TX "dst=" MACSTR ++ " freq=%u type=%d", MAC2STR(src), freq, ++ DPP_PA_PKEX_COMMIT_REVEAL_REQ); ++ hostapd_drv_send_action(hapd, freq, 0, src, ++ wpabuf_head(msg), wpabuf_len(msg)); ++ wpabuf_free(msg); ++} ++ ++ ++static void ++hostapd_dpp_rx_pkex_commit_reveal_req(struct hostapd_data *hapd, const u8 *src, ++ const u8 *hdr, const u8 *buf, size_t len, ++ unsigned int freq) ++{ ++ struct wpabuf *msg; ++ struct dpp_pkex *pkex = hapd->dpp_pkex; ++ struct dpp_bootstrap_info *bi; ++ ++ wpa_printf(MSG_DEBUG, "DPP: PKEX Commit-Reveal Request from " MACSTR, ++ MAC2STR(src)); ++ ++ if (!pkex || pkex->initiator || !pkex->exchange_done) { ++ wpa_printf(MSG_DEBUG, "DPP: No matching PKEX session"); ++ return; ++ } ++ ++ msg = dpp_pkex_rx_commit_reveal_req(pkex, hdr, buf, len); ++ if (!msg) { ++ wpa_printf(MSG_DEBUG, "DPP: Failed to process the request"); ++ if (hapd->dpp_pkex->failed) { ++ wpa_printf(MSG_DEBUG, "DPP: Terminate PKEX exchange"); ++ if (hapd->dpp_pkex->t > hapd->dpp_pkex->own_bi->pkex_t) ++ hapd->dpp_pkex->own_bi->pkex_t = ++ hapd->dpp_pkex->t; ++ dpp_pkex_free(hapd->dpp_pkex); ++ hapd->dpp_pkex = NULL; ++ } ++ return; ++ } ++ ++ wpa_printf(MSG_DEBUG, "DPP: Send PKEX Commit-Reveal Response to " ++ MACSTR, MAC2STR(src)); ++ ++ wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_TX "dst=" MACSTR ++ " freq=%u type=%d", MAC2STR(src), freq, ++ DPP_PA_PKEX_COMMIT_REVEAL_RESP); ++ hostapd_drv_send_action(hapd, freq, 0, src, ++ wpabuf_head(msg), wpabuf_len(msg)); ++ wpabuf_free(msg); ++ ++ bi = dpp_pkex_finish(hapd->iface->interfaces->dpp, pkex, src, freq); ++ if (!bi) ++ return; ++ hapd->dpp_pkex = NULL; ++} ++ ++ ++static void ++hostapd_dpp_rx_pkex_commit_reveal_resp(struct hostapd_data *hapd, const u8 *src, ++ const u8 *hdr, const u8 *buf, size_t len, ++ unsigned int freq) ++{ ++ int res; ++ struct dpp_bootstrap_info *bi; ++ struct dpp_pkex *pkex = hapd->dpp_pkex; ++ char cmd[500]; ++ ++ wpa_printf(MSG_DEBUG, "DPP: PKEX Commit-Reveal Response from " MACSTR, ++ MAC2STR(src)); ++ ++ if (!pkex || !pkex->initiator || !pkex->exchange_done) { ++ wpa_printf(MSG_DEBUG, "DPP: No matching PKEX session"); ++ return; ++ } ++ ++ res = dpp_pkex_rx_commit_reveal_resp(pkex, hdr, buf, len); ++ if (res < 0) { ++ wpa_printf(MSG_DEBUG, "DPP: Failed to process the response"); ++ return; ++ } ++ ++ bi = dpp_pkex_finish(hapd->iface->interfaces->dpp, pkex, src, freq); ++ if (!bi) ++ return; ++ hapd->dpp_pkex = NULL; ++ ++ os_snprintf(cmd, sizeof(cmd), " peer=%u %s", ++ bi->id, ++ hapd->dpp_pkex_auth_cmd ? hapd->dpp_pkex_auth_cmd : ""); ++ wpa_printf(MSG_DEBUG, ++ "DPP: Start authentication after PKEX with parameters: %s", ++ cmd); ++ if (hostapd_dpp_auth_init(hapd, cmd) < 0) { ++ wpa_printf(MSG_DEBUG, ++ "DPP: Authentication initialization failed"); ++ return; ++ } ++} ++ ++ ++void hostapd_dpp_rx_action(struct hostapd_data *hapd, const u8 *src, ++ const u8 *buf, size_t len, unsigned int freq) ++{ ++ u8 crypto_suite; ++ enum dpp_public_action_frame_type type; ++ const u8 *hdr; ++ unsigned int pkex_t; ++ ++ if (len < DPP_HDR_LEN) ++ return; ++ if (WPA_GET_BE24(buf) != OUI_WFA || buf[3] != DPP_OUI_TYPE) ++ return; ++ hdr = buf; ++ buf += 4; ++ len -= 4; ++ crypto_suite = *buf++; ++ type = *buf++; ++ len -= 2; ++ ++ wpa_printf(MSG_DEBUG, ++ "DPP: Received DPP Public Action frame crypto suite %u type %d from " ++ MACSTR " freq=%u", ++ crypto_suite, type, MAC2STR(src), freq); ++ if (crypto_suite != 1) { ++ wpa_printf(MSG_DEBUG, "DPP: Unsupported crypto suite %u", ++ crypto_suite); ++ wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_RX "src=" MACSTR ++ " freq=%u type=%d ignore=unsupported-crypto-suite", ++ MAC2STR(src), freq, type); ++ return; ++ } ++ wpa_hexdump(MSG_MSGDUMP, "DPP: Received message attributes", buf, len); ++ if (dpp_check_attrs(buf, len) < 0) { ++ wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_RX "src=" MACSTR ++ " freq=%u type=%d ignore=invalid-attributes", ++ MAC2STR(src), freq, type); ++ return; ++ } ++ wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_RX "src=" MACSTR ++ " freq=%u type=%d", MAC2STR(src), freq, type); ++ ++ switch (type) { ++ case DPP_PA_AUTHENTICATION_REQ: ++ hostapd_dpp_rx_auth_req(hapd, src, hdr, buf, len, freq); ++ break; ++ case DPP_PA_AUTHENTICATION_RESP: ++ hostapd_dpp_rx_auth_resp(hapd, src, hdr, buf, len, freq); ++ break; ++ case DPP_PA_AUTHENTICATION_CONF: ++ hostapd_dpp_rx_auth_conf(hapd, src, hdr, buf, len); ++ break; ++ case DPP_PA_PEER_DISCOVERY_REQ: ++ hostapd_dpp_rx_peer_disc_req(hapd, src, buf, len, freq); ++ break; ++ case DPP_PA_PKEX_EXCHANGE_REQ: ++ hostapd_dpp_rx_pkex_exchange_req(hapd, src, buf, len, freq); ++ break; ++ case DPP_PA_PKEX_EXCHANGE_RESP: ++ hostapd_dpp_rx_pkex_exchange_resp(hapd, src, buf, len, freq); ++ break; ++ case DPP_PA_PKEX_COMMIT_REVEAL_REQ: ++ hostapd_dpp_rx_pkex_commit_reveal_req(hapd, src, hdr, buf, len, ++ freq); ++ break; ++ case DPP_PA_PKEX_COMMIT_REVEAL_RESP: ++ hostapd_dpp_rx_pkex_commit_reveal_resp(hapd, src, hdr, buf, len, ++ freq); ++ break; ++#ifdef CONFIG_DPP2 ++ case DPP_PA_CONFIGURATION_RESULT: ++ hostapd_dpp_rx_conf_result(hapd, src, hdr, buf, len); ++ break; ++#endif /* CONFIG_DPP2 */ ++ default: ++ wpa_printf(MSG_DEBUG, ++ "DPP: Ignored unsupported frame subtype %d", type); ++ break; ++ } ++ ++ if (hapd->dpp_pkex) ++ pkex_t = hapd->dpp_pkex->t; ++ else if (hapd->dpp_pkex_bi) ++ pkex_t = hapd->dpp_pkex_bi->pkex_t; ++ else ++ pkex_t = 0; ++ if (pkex_t >= PKEX_COUNTER_T_LIMIT) { ++ wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_PKEX_T_LIMIT "id=0"); ++ hostapd_dpp_pkex_remove(hapd, "*"); ++ } ++} ++ ++ ++struct wpabuf * ++hostapd_dpp_gas_req_handler(struct hostapd_data *hapd, const u8 *sa, ++ const u8 *query, size_t query_len) ++{ ++ struct dpp_authentication *auth = hapd->dpp_auth; ++ struct wpabuf *resp; ++ ++ wpa_printf(MSG_DEBUG, "DPP: GAS request from " MACSTR, MAC2STR(sa)); ++ if (!auth || !auth->auth_success || ++ os_memcmp(sa, auth->peer_mac_addr, ETH_ALEN) != 0) { ++ wpa_printf(MSG_DEBUG, "DPP: No matching exchange in progress"); ++ return NULL; ++ } ++ wpa_hexdump(MSG_DEBUG, ++ "DPP: Received Configuration Request (GAS Query Request)", ++ query, query_len); ++ wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONF_REQ_RX "src=" MACSTR, ++ MAC2STR(sa)); ++ resp = dpp_conf_req_rx(auth, query, query_len); ++ if (!resp) ++ wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONF_FAILED); ++ return resp; ++} ++ ++ ++void hostapd_dpp_gas_status_handler(struct hostapd_data *hapd, int ok) ++{ ++ struct dpp_authentication *auth = hapd->dpp_auth; ++ ++ if (!auth) ++ return; ++ ++ wpa_printf(MSG_DEBUG, "DPP: Configuration exchange completed (ok=%d)", ++ ok); ++ eloop_cancel_timeout(hostapd_dpp_reply_wait_timeout, hapd, NULL); ++ eloop_cancel_timeout(hostapd_dpp_auth_resp_retry_timeout, hapd, NULL); ++#ifdef CONFIG_DPP2 ++ if (ok && auth->peer_version >= 2 && ++ auth->conf_resp_status == DPP_STATUS_OK) { ++ wpa_printf(MSG_DEBUG, "DPP: Wait for Configuration Result"); ++ auth->waiting_conf_result = 1; ++ eloop_cancel_timeout(hostapd_dpp_config_result_wait_timeout, ++ hapd, NULL); ++ eloop_register_timeout(2, 0, ++ hostapd_dpp_config_result_wait_timeout, ++ hapd, NULL); ++ return; ++ } ++#endif /* CONFIG_DPP2 */ ++ hostapd_drv_send_action_cancel_wait(hapd); ++ ++ if (ok) ++ wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONF_SENT); ++ else ++ wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONF_FAILED); ++ dpp_auth_deinit(hapd->dpp_auth); ++ hapd->dpp_auth = NULL; ++} ++ ++ ++int hostapd_dpp_configurator_sign(struct hostapd_data *hapd, const char *cmd) ++{ ++ struct dpp_authentication *auth; ++ int ret = -1; ++ char *curve = NULL; ++ ++ auth = os_zalloc(sizeof(*auth)); ++ if (!auth) ++ return -1; ++ ++ curve = get_param(cmd, " curve="); ++ hostapd_dpp_set_testing_options(hapd, auth); ++ if (dpp_set_configurator(hapd->iface->interfaces->dpp, hapd->msg_ctx, ++ auth, cmd) == 0 && ++ dpp_configurator_own_config(auth, curve, 1) == 0) { ++ hostapd_dpp_handle_config_obj(hapd, auth); ++ ret = 0; ++ } ++ ++ dpp_auth_deinit(auth); ++ os_free(curve); ++ ++ return ret; ++} ++ ++ ++int hostapd_dpp_pkex_add(struct hostapd_data *hapd, const char *cmd) ++{ ++ struct dpp_bootstrap_info *own_bi; ++ const char *pos, *end; ++ ++ pos = os_strstr(cmd, " own="); ++ if (!pos) ++ return -1; ++ pos += 5; ++ own_bi = dpp_bootstrap_get_id(hapd->iface->interfaces->dpp, atoi(pos)); ++ if (!own_bi) { ++ wpa_printf(MSG_DEBUG, ++ "DPP: Identified bootstrap info not found"); ++ return -1; ++ } ++ if (own_bi->type != DPP_BOOTSTRAP_PKEX) { ++ wpa_printf(MSG_DEBUG, ++ "DPP: Identified bootstrap info not for PKEX"); ++ return -1; ++ } ++ hapd->dpp_pkex_bi = own_bi; ++ own_bi->pkex_t = 0; /* clear pending errors on new code */ ++ ++ os_free(hapd->dpp_pkex_identifier); ++ hapd->dpp_pkex_identifier = NULL; ++ pos = os_strstr(cmd, " identifier="); ++ if (pos) { ++ pos += 12; ++ end = os_strchr(pos, ' '); ++ if (!end) ++ return -1; ++ hapd->dpp_pkex_identifier = os_malloc(end - pos + 1); ++ if (!hapd->dpp_pkex_identifier) ++ return -1; ++ os_memcpy(hapd->dpp_pkex_identifier, pos, end - pos); ++ hapd->dpp_pkex_identifier[end - pos] = '\0'; ++ } ++ ++ pos = os_strstr(cmd, " code="); ++ if (!pos) ++ return -1; ++ os_free(hapd->dpp_pkex_code); ++ hapd->dpp_pkex_code = os_strdup(pos + 6); ++ if (!hapd->dpp_pkex_code) ++ return -1; ++ ++ if (os_strstr(cmd, " init=1")) { ++ struct wpabuf *msg; ++ ++ wpa_printf(MSG_DEBUG, "DPP: Initiating PKEX"); ++ dpp_pkex_free(hapd->dpp_pkex); ++ hapd->dpp_pkex = dpp_pkex_init(hapd->msg_ctx, own_bi, ++ hapd->own_addr, ++ hapd->dpp_pkex_identifier, ++ hapd->dpp_pkex_code); ++ if (!hapd->dpp_pkex) ++ return -1; ++ ++ msg = hapd->dpp_pkex->exchange_req; ++ /* TODO: Which channel to use? */ ++ wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_TX "dst=" MACSTR ++ " freq=%u type=%d", MAC2STR(broadcast), 2437, ++ DPP_PA_PKEX_EXCHANGE_REQ); ++ hostapd_drv_send_action(hapd, 2437, 0, broadcast, ++ wpabuf_head(msg), wpabuf_len(msg)); ++ } ++ ++ /* TODO: Support multiple PKEX info entries */ ++ ++ os_free(hapd->dpp_pkex_auth_cmd); ++ hapd->dpp_pkex_auth_cmd = os_strdup(cmd); ++ ++ return 1; ++} ++ ++ ++int hostapd_dpp_pkex_remove(struct hostapd_data *hapd, const char *id) ++{ ++ unsigned int id_val; ++ ++ if (os_strcmp(id, "*") == 0) { ++ id_val = 0; ++ } else { ++ id_val = atoi(id); ++ if (id_val == 0) ++ return -1; ++ } ++ ++ if ((id_val != 0 && id_val != 1) || !hapd->dpp_pkex_code) ++ return -1; ++ ++ /* TODO: Support multiple PKEX entries */ ++ os_free(hapd->dpp_pkex_code); ++ hapd->dpp_pkex_code = NULL; ++ os_free(hapd->dpp_pkex_identifier); ++ hapd->dpp_pkex_identifier = NULL; ++ os_free(hapd->dpp_pkex_auth_cmd); ++ hapd->dpp_pkex_auth_cmd = NULL; ++ hapd->dpp_pkex_bi = NULL; ++ /* TODO: Remove dpp_pkex only if it is for the identified PKEX code */ ++ dpp_pkex_free(hapd->dpp_pkex); ++ hapd->dpp_pkex = NULL; ++ return 0; ++} ++ ++ ++void hostapd_dpp_stop(struct hostapd_data *hapd) ++{ ++ dpp_auth_deinit(hapd->dpp_auth); ++ hapd->dpp_auth = NULL; ++ dpp_pkex_free(hapd->dpp_pkex); ++ hapd->dpp_pkex = NULL; ++} ++ ++ ++int hostapd_dpp_init(struct hostapd_data *hapd) ++{ ++ hapd->dpp_allowed_roles = DPP_CAPAB_CONFIGURATOR | DPP_CAPAB_ENROLLEE; ++ hapd->dpp_init_done = 1; ++ return 0; ++} ++ ++ ++void hostapd_dpp_deinit(struct hostapd_data *hapd) ++{ ++#ifdef CONFIG_TESTING_OPTIONS ++ os_free(hapd->dpp_config_obj_override); ++ hapd->dpp_config_obj_override = NULL; ++ os_free(hapd->dpp_discovery_override); ++ hapd->dpp_discovery_override = NULL; ++ os_free(hapd->dpp_groups_override); ++ hapd->dpp_groups_override = NULL; ++ hapd->dpp_ignore_netaccesskey_mismatch = 0; ++#endif /* CONFIG_TESTING_OPTIONS */ ++ if (!hapd->dpp_init_done) ++ return; ++ eloop_cancel_timeout(hostapd_dpp_reply_wait_timeout, hapd, NULL); ++ eloop_cancel_timeout(hostapd_dpp_init_timeout, hapd, NULL); ++ eloop_cancel_timeout(hostapd_dpp_auth_resp_retry_timeout, hapd, NULL); ++#ifdef CONFIG_DPP2 ++ eloop_cancel_timeout(hostapd_dpp_config_result_wait_timeout, hapd, ++ NULL); ++#endif /* CONFIG_DPP2 */ ++ dpp_auth_deinit(hapd->dpp_auth); ++ hapd->dpp_auth = NULL; ++ hostapd_dpp_pkex_remove(hapd, "*"); ++ hapd->dpp_pkex = NULL; ++ os_free(hapd->dpp_configurator_params); ++ hapd->dpp_configurator_params = NULL; ++} +--- contrib/wpa/src/ap/dpp_hostapd.h.orig ++++ contrib/wpa/src/ap/dpp_hostapd.h +@@ -0,0 +1,37 @@ ++/* ++ * hostapd / DPP integration ++ * Copyright (c) 2017, Qualcomm Atheros, Inc. ++ * ++ * This software may be distributed under the terms of the BSD license. ++ * See README for more details. ++ */ ++ ++#ifndef DPP_HOSTAPD_H ++#define DPP_HOSTAPD_H ++ ++int hostapd_dpp_qr_code(struct hostapd_data *hapd, const char *cmd); ++int hostapd_dpp_auth_init(struct hostapd_data *hapd, const char *cmd); ++int hostapd_dpp_listen(struct hostapd_data *hapd, const char *cmd); ++void hostapd_dpp_listen_stop(struct hostapd_data *hapd); ++void hostapd_dpp_rx_action(struct hostapd_data *hapd, const u8 *src, ++ const u8 *buf, size_t len, unsigned int freq); ++void hostapd_dpp_tx_status(struct hostapd_data *hapd, const u8 *dst, ++ const u8 *data, size_t data_len, int ok); ++struct wpabuf * ++hostapd_dpp_gas_req_handler(struct hostapd_data *hapd, const u8 *sa, ++ const u8 *query, size_t query_len); ++void hostapd_dpp_gas_status_handler(struct hostapd_data *hapd, int ok); ++int hostapd_dpp_configurator_add(struct hostapd_data *hapd, const char *cmd); ++int hostapd_dpp_configurator_remove(struct hostapd_data *hapd, const char *id); ++int hostapd_dpp_configurator_sign(struct hostapd_data *hapd, const char *cmd); ++int hostapd_dpp_configurator_get_key(struct hostapd_data *hapd, unsigned int id, ++ char *buf, size_t buflen); ++int hostapd_dpp_pkex_add(struct hostapd_data *hapd, const char *cmd); ++int hostapd_dpp_pkex_remove(struct hostapd_data *hapd, const char *id); ++void hostapd_dpp_stop(struct hostapd_data *hapd); ++int hostapd_dpp_init(struct hostapd_data *hapd); ++void hostapd_dpp_deinit(struct hostapd_data *hapd); ++void hostapd_dpp_init_global(struct hapd_interfaces *ifaces); ++void hostapd_dpp_deinit_global(struct hapd_interfaces *ifaces); ++ ++#endif /* DPP_HOSTAPD_H */ +--- contrib/wpa/src/ap/drv_callbacks.c.orig ++++ contrib/wpa/src/ap/drv_callbacks.c +@@ -15,6 +15,7 @@ + #include "common/ieee802_11_defs.h" + #include "common/ieee802_11_common.h" + #include "common/wpa_ctrl.h" ++#include "common/dpp.h" + #include "crypto/random.h" + #include "p2p/p2p.h" + #include "wps/wps.h" +@@ -22,6 +23,7 @@ + #include "wnm_ap.h" + #include "hostapd.h" + #include "ieee802_11.h" ++#include "ieee802_11_auth.h" + #include "sta_info.h" + #include "accounting.h" + #include "tkip_countermeasures.h" +@@ -30,11 +32,77 @@ + #include "wps_hostapd.h" + #include "ap_drv_ops.h" + #include "ap_config.h" ++#include "ap_mlme.h" + #include "hw_features.h" + #include "dfs.h" + #include "beacon.h" ++#include "mbo_ap.h" ++#include "dpp_hostapd.h" ++#include "fils_hlp.h" ++#include "neighbor_db.h" + + ++#ifdef CONFIG_FILS ++void hostapd_notify_assoc_fils_finish(struct hostapd_data *hapd, ++ struct sta_info *sta) ++{ ++ u16 reply_res = WLAN_STATUS_SUCCESS; ++ struct ieee802_11_elems elems; ++ u8 buf[IEEE80211_MAX_MMPDU_SIZE], *p = buf; ++ int new_assoc; ++ ++ wpa_printf(MSG_DEBUG, "%s FILS: Finish association with " MACSTR, ++ __func__, MAC2STR(sta->addr)); ++ eloop_cancel_timeout(fils_hlp_timeout, hapd, sta); ++ if (!sta->fils_pending_assoc_req) ++ return; ++ ++ ieee802_11_parse_elems(sta->fils_pending_assoc_req, ++ sta->fils_pending_assoc_req_len, &elems, 0); ++ if (!elems.fils_session) { ++ wpa_printf(MSG_DEBUG, "%s failed to find FILS Session element", ++ __func__); ++ return; ++ } ++ ++ p = hostapd_eid_assoc_fils_session(sta->wpa_sm, p, ++ elems.fils_session, ++ sta->fils_hlp_resp); ++ ++ reply_res = hostapd_sta_assoc(hapd, sta->addr, ++ sta->fils_pending_assoc_is_reassoc, ++ WLAN_STATUS_SUCCESS, ++ buf, p - buf); ++ ap_sta_set_authorized(hapd, sta, 1); ++ new_assoc = (sta->flags & WLAN_STA_ASSOC) == 0; ++ sta->flags |= WLAN_STA_AUTH | WLAN_STA_ASSOC; ++ sta->flags &= ~WLAN_STA_WNM_SLEEP_MODE; ++ hostapd_set_sta_flags(hapd, sta); ++ wpa_auth_sm_event(sta->wpa_sm, WPA_ASSOC_FILS); ++ ieee802_1x_notify_port_enabled(sta->eapol_sm, 1); ++ hostapd_new_assoc_sta(hapd, sta, !new_assoc); ++ os_free(sta->fils_pending_assoc_req); ++ sta->fils_pending_assoc_req = NULL; ++ sta->fils_pending_assoc_req_len = 0; ++ wpabuf_free(sta->fils_hlp_resp); ++ sta->fils_hlp_resp = NULL; ++ wpabuf_free(sta->hlp_dhcp_discover); ++ sta->hlp_dhcp_discover = NULL; ++ fils_hlp_deinit(hapd); ++ ++ /* ++ * Remove the station in case transmission of a success response fails ++ * (the STA was added associated to the driver) or if the station was ++ * previously added unassociated. ++ */ ++ if (reply_res != WLAN_STATUS_SUCCESS || sta->added_unassoc) { ++ hostapd_drv_sta_remove(hapd, sta->addr); ++ sta->added_unassoc = 0; ++ } ++} ++#endif /* CONFIG_FILS */ ++ ++ + int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr, + const u8 *req_ies, size_t req_ies_len, int reassoc) + { +@@ -43,10 +111,10 @@ + struct ieee802_11_elems elems; + const u8 *ie; + size_t ielen; +-#if defined(CONFIG_IEEE80211R) || defined(CONFIG_IEEE80211W) ++#if defined(CONFIG_IEEE80211R_AP) || defined(CONFIG_IEEE80211W) || defined(CONFIG_FILS) || defined(CONFIG_OWE) + u8 buf[sizeof(struct ieee80211_mgmt) + 1024]; + u8 *p = buf; +-#endif /* CONFIG_IEEE80211R || CONFIG_IEEE80211W */ ++#endif /* CONFIG_IEEE80211R_AP || CONFIG_IEEE80211W || CONFIG_FILS || CONFIG_OWE */ + u16 reason = WLAN_REASON_UNSPECIFIED; + u16 status = WLAN_STATUS_SUCCESS; + const u8 *p2p_dev_addr = NULL; +@@ -114,6 +182,21 @@ + } + sta->flags &= ~(WLAN_STA_WPS | WLAN_STA_MAYBE_WPS | WLAN_STA_WPS2); + ++ /* ++ * ACL configurations to the drivers (implementing AP SME and ACL ++ * offload) without hostapd's knowledge, can result in a disconnection ++ * though the driver accepts the connection. Skip the hostapd check for ++ * ACL if the driver supports ACL offload to avoid potentially ++ * conflicting ACL rules. ++ */ ++ if (hapd->iface->drv_max_acl_mac_addrs == 0 && ++ hostapd_check_acl(hapd, addr, NULL) != HOSTAPD_ACL_ACCEPT) { ++ wpa_printf(MSG_INFO, "STA " MACSTR " not allowed to connect", ++ MAC2STR(addr)); ++ reason = WLAN_REASON_UNSPECIFIED; ++ goto fail; ++ } ++ + #ifdef CONFIG_P2P + if (elems.p2p) { + wpabuf_free(sta->p2p_ie); +@@ -154,6 +237,14 @@ + elems.hs20_len - 4); + } else + sta->hs20_ie = NULL; ++ ++ wpabuf_free(sta->roaming_consortium); ++ if (elems.roaming_cons_sel) ++ sta->roaming_consortium = wpabuf_alloc_copy( ++ elems.roaming_cons_sel + 4, ++ elems.roaming_cons_sel_len - 4); ++ else ++ sta->roaming_consortium = NULL; + #endif /* CONFIG_HS20 */ + + #ifdef CONFIG_FST +@@ -164,6 +255,11 @@ + sta->mb_ies = NULL; + #endif /* CONFIG_FST */ + ++ mbo_ap_check_sta_assoc(hapd, sta, &elems); ++ ++ ap_copy_sta_supp_op_classes(sta, elems.supp_op_classes, ++ elems.supp_op_classes_len); ++ + if (hapd->conf->wpa) { + if (ie == NULL || ielen == 0) { + #ifdef CONFIG_WPS +@@ -176,7 +272,9 @@ + #endif /* CONFIG_WPS */ + + wpa_printf(MSG_DEBUG, "No WPA/RSN IE from STA"); +- return -1; ++ reason = WLAN_REASON_INVALID_IE; ++ status = WLAN_STATUS_INVALID_IE; ++ goto fail; + } + #ifdef CONFIG_WPS + if (hapd->conf->wps_state && ie[0] == 0xdd && ie[1] >= 4 && +@@ -208,8 +306,10 @@ + return -1; + } + res = wpa_validate_wpa_ie(hapd->wpa_auth, sta->wpa_sm, ++ hapd->iface->freq, + ie, ielen, +- elems.mdie, elems.mdie_len); ++ elems.mdie, elems.mdie_len, ++ elems.owe_dh, elems.owe_dh_len); + if (res != WPA_IE_OK) { + wpa_printf(MSG_DEBUG, + "WPA/RSN information element rejected? (res %u)", +@@ -230,8 +330,8 @@ + reason = WLAN_REASON_INVALID_IE; + status = WLAN_STATUS_INVALID_IE; + } else if (res == WPA_INVALID_MGMT_GROUP_CIPHER) { +- reason = WLAN_REASON_GROUP_CIPHER_NOT_VALID; +- status = WLAN_STATUS_GROUP_CIPHER_NOT_VALID; ++ reason = WLAN_REASON_CIPHER_SUITE_REJECTED; ++ status = WLAN_STATUS_CIPHER_REJECTED_PER_POLICY; + } + #endif /* CONFIG_IEEE80211W */ + else { +@@ -241,10 +341,14 @@ + goto fail; + } + #ifdef CONFIG_IEEE80211W +- if ((sta->flags & WLAN_STA_MFP) && !sta->sa_query_timed_out && ++ if ((sta->flags & (WLAN_STA_ASSOC | WLAN_STA_MFP)) == ++ (WLAN_STA_ASSOC | WLAN_STA_MFP) && ++ !sta->sa_query_timed_out && + sta->sa_query_count > 0) + ap_check_sa_query_timeout(hapd, sta); +- if ((sta->flags & WLAN_STA_MFP) && !sta->sa_query_timed_out && ++ if ((sta->flags & (WLAN_STA_ASSOC | WLAN_STA_MFP)) == ++ (WLAN_STA_ASSOC | WLAN_STA_MFP) && ++ !sta->sa_query_timed_out && + (sta->auth_alg != WLAN_AUTH_FT)) { + /* + * STA has already been associated with MFP and SA +@@ -271,7 +375,7 @@ + sta->flags &= ~WLAN_STA_MFP; + #endif /* CONFIG_IEEE80211W */ + +-#ifdef CONFIG_IEEE80211R ++#ifdef CONFIG_IEEE80211R_AP + if (sta->auth_alg == WLAN_AUTH_FT) { + status = wpa_ft_validate_reassoc(sta->wpa_sm, req_ies, + req_ies_len); +@@ -285,7 +389,7 @@ + goto fail; + } + } +-#endif /* CONFIG_IEEE80211R */ ++#endif /* CONFIG_IEEE80211R_AP */ + } else if (hapd->conf->wps_state) { + #ifdef CONFIG_WPS + struct wpabuf *wps; +@@ -338,23 +442,175 @@ + return WLAN_STATUS_INVALID_IE; + #endif /* CONFIG_HS20 */ + } ++ ++#ifdef CONFIG_MBO ++ if (hapd->conf->mbo_enabled && (hapd->conf->wpa & 2) && ++ elems.mbo && sta->cell_capa && !(sta->flags & WLAN_STA_MFP) && ++ hapd->conf->ieee80211w != NO_MGMT_FRAME_PROTECTION) { ++ wpa_printf(MSG_INFO, ++ "MBO: Reject WPA2 association without PMF"); ++ return WLAN_STATUS_UNSPECIFIED_FAILURE; ++ } ++#endif /* CONFIG_MBO */ ++ + #ifdef CONFIG_WPS + skip_wpa_check: + #endif /* CONFIG_WPS */ + +-#ifdef CONFIG_IEEE80211R ++#ifdef CONFIG_IEEE80211R_AP + p = wpa_sm_write_assoc_resp_ies(sta->wpa_sm, buf, sizeof(buf), + sta->auth_alg, req_ies, req_ies_len); ++ if (!p) { ++ wpa_printf(MSG_DEBUG, "FT: Failed to write AssocResp IEs"); ++ return WLAN_STATUS_UNSPECIFIED_FAILURE; ++ } ++#endif /* CONFIG_IEEE80211R_AP */ + ++#ifdef CONFIG_FILS ++ if (sta->auth_alg == WLAN_AUTH_FILS_SK || ++ sta->auth_alg == WLAN_AUTH_FILS_SK_PFS || ++ sta->auth_alg == WLAN_AUTH_FILS_PK) { ++ int delay_assoc = 0; ++ ++ if (!req_ies) ++ return WLAN_STATUS_UNSPECIFIED_FAILURE; ++ ++ if (!wpa_fils_validate_fils_session(sta->wpa_sm, req_ies, ++ req_ies_len, ++ sta->fils_session)) { ++ wpa_printf(MSG_DEBUG, ++ "FILS: Session validation failed"); ++ return WLAN_STATUS_UNSPECIFIED_FAILURE; ++ } ++ ++ res = wpa_fils_validate_key_confirm(sta->wpa_sm, req_ies, ++ req_ies_len); ++ if (res < 0) { ++ wpa_printf(MSG_DEBUG, ++ "FILS: Key Confirm validation failed"); ++ return WLAN_STATUS_UNSPECIFIED_FAILURE; ++ } ++ ++ if (fils_process_hlp(hapd, sta, req_ies, req_ies_len) > 0) { ++ wpa_printf(MSG_DEBUG, ++ "FILS: Delaying Assoc Response (HLP)"); ++ delay_assoc = 1; ++ } else { ++ wpa_printf(MSG_DEBUG, ++ "FILS: Going ahead with Assoc Response (no HLP)"); ++ } ++ ++ if (sta) { ++ wpa_printf(MSG_DEBUG, "FILS: HLP callback cleanup"); ++ eloop_cancel_timeout(fils_hlp_timeout, hapd, sta); ++ os_free(sta->fils_pending_assoc_req); ++ sta->fils_pending_assoc_req = NULL; ++ sta->fils_pending_assoc_req_len = 0; ++ wpabuf_free(sta->fils_hlp_resp); ++ sta->fils_hlp_resp = NULL; ++ sta->fils_drv_assoc_finish = 0; ++ } ++ ++ if (sta && delay_assoc && status == WLAN_STATUS_SUCCESS) { ++ u8 *req_tmp; ++ ++ req_tmp = os_malloc(req_ies_len); ++ if (!req_tmp) { ++ wpa_printf(MSG_DEBUG, ++ "FILS: buffer allocation failed for assoc req"); ++ goto fail; ++ } ++ os_memcpy(req_tmp, req_ies, req_ies_len); ++ sta->fils_pending_assoc_req = req_tmp; ++ sta->fils_pending_assoc_req_len = req_ies_len; ++ sta->fils_pending_assoc_is_reassoc = reassoc; ++ sta->fils_drv_assoc_finish = 1; ++ wpa_printf(MSG_DEBUG, ++ "FILS: Waiting for HLP processing before sending (Re)Association Response frame to " ++ MACSTR, MAC2STR(sta->addr)); ++ eloop_register_timeout( ++ 0, hapd->conf->fils_hlp_wait_time * 1024, ++ fils_hlp_timeout, hapd, sta); ++ return 0; ++ } ++ p = hostapd_eid_assoc_fils_session(sta->wpa_sm, p, ++ elems.fils_session, ++ sta->fils_hlp_resp); ++ wpa_hexdump(MSG_DEBUG, "FILS Assoc Resp BUF (IEs)", ++ buf, p - buf); ++ } ++#endif /* CONFIG_FILS */ ++ ++#ifdef CONFIG_OWE ++ if ((hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_OWE) && ++ wpa_auth_sta_key_mgmt(sta->wpa_sm) == WPA_KEY_MGMT_OWE && ++ elems.owe_dh) { ++ u8 *npos; ++ ++ npos = owe_assoc_req_process(hapd, sta, ++ elems.owe_dh, elems.owe_dh_len, ++ p, sizeof(buf) - (p - buf), ++ &reason); ++ if (npos) ++ p = npos; ++ if (!npos && ++ reason == WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED) { ++ status = WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED; ++ hostapd_sta_assoc(hapd, addr, reassoc, status, buf, ++ p - buf); ++ return 0; ++ } ++ ++ if (!npos || reason != WLAN_STATUS_SUCCESS) ++ goto fail; ++ } ++#endif /* CONFIG_OWE */ ++ ++#ifdef CONFIG_DPP2 ++ dpp_pfs_free(sta->dpp_pfs); ++ sta->dpp_pfs = NULL; ++ ++ if ((hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_DPP) && ++ hapd->conf->dpp_netaccesskey && sta->wpa_sm && ++ wpa_auth_sta_key_mgmt(sta->wpa_sm) == WPA_KEY_MGMT_DPP && ++ elems.owe_dh) { ++ sta->dpp_pfs = dpp_pfs_init( ++ wpabuf_head(hapd->conf->dpp_netaccesskey), ++ wpabuf_len(hapd->conf->dpp_netaccesskey)); ++ if (!sta->dpp_pfs) { ++ wpa_printf(MSG_DEBUG, ++ "DPP: Could not initialize PFS"); ++ /* Try to continue without PFS */ ++ goto pfs_fail; ++ } ++ ++ if (dpp_pfs_process(sta->dpp_pfs, elems.owe_dh, ++ elems.owe_dh_len) < 0) { ++ dpp_pfs_free(sta->dpp_pfs); ++ sta->dpp_pfs = NULL; ++ reason = WLAN_REASON_UNSPECIFIED; ++ goto fail; ++ } ++ } ++ ++ wpa_auth_set_dpp_z(sta->wpa_sm, sta->dpp_pfs ? ++ sta->dpp_pfs->secret : NULL); ++ pfs_fail: ++#endif /* CONFIG_DPP2 */ ++ ++#if defined(CONFIG_IEEE80211R_AP) || defined(CONFIG_FILS) || defined(CONFIG_OWE) + hostapd_sta_assoc(hapd, addr, reassoc, status, buf, p - buf); + +- if (sta->auth_alg == WLAN_AUTH_FT) ++ if (sta->auth_alg == WLAN_AUTH_FT || ++ sta->auth_alg == WLAN_AUTH_FILS_SK || ++ sta->auth_alg == WLAN_AUTH_FILS_SK_PFS || ++ sta->auth_alg == WLAN_AUTH_FILS_PK) + ap_sta_set_authorized(hapd, sta, 1); +-#else /* CONFIG_IEEE80211R */ ++#else /* CONFIG_IEEE80211R_AP || CONFIG_FILS */ + /* Keep compiler silent about unused variables */ + if (status) { + } +-#endif /* CONFIG_IEEE80211R */ ++#endif /* CONFIG_IEEE80211R_AP || CONFIG_FILS */ + + new_assoc = (sta->flags & WLAN_STA_ASSOC) == 0; + sta->flags |= WLAN_STA_AUTH | WLAN_STA_ASSOC; +@@ -364,6 +620,12 @@ + + if (reassoc && (sta->auth_alg == WLAN_AUTH_FT)) + wpa_auth_sm_event(sta->wpa_sm, WPA_ASSOC_FT); ++#ifdef CONFIG_FILS ++ else if (sta->auth_alg == WLAN_AUTH_FILS_SK || ++ sta->auth_alg == WLAN_AUTH_FILS_SK_PFS || ++ sta->auth_alg == WLAN_AUTH_FILS_PK) ++ wpa_auth_sm_event(sta->wpa_sm, WPA_ASSOC_FILS); ++#endif /* CONFIG_FILS */ + else + wpa_auth_sm_event(sta->wpa_sm, WPA_ASSOC); + +@@ -381,9 +643,9 @@ + return 0; + + fail: +-#ifdef CONFIG_IEEE80211R ++#ifdef CONFIG_IEEE80211R_AP + hostapd_sta_assoc(hapd, addr, reassoc, status, buf, p - buf); +-#endif /* CONFIG_IEEE80211R */ ++#endif /* CONFIG_IEEE80211R_AP */ + hostapd_drv_sta_disassoc(hapd, sta->addr, reason); + ap_free_sta(hapd, sta); + return -1; +@@ -431,7 +693,7 @@ + { + struct sta_info *sta = ap_get_sta(hapd, addr); + +- if (!sta || !hapd->conf->disassoc_low_ack) ++ if (!sta || !hapd->conf->disassoc_low_ack || sta->agreed_to_steer) + return; + + hostapd_logger(hapd, addr, HOSTAPD_MODULE_IEEE80211, +@@ -438,22 +700,92 @@ + HOSTAPD_LEVEL_INFO, + "disconnected due to excessive missing ACKs"); + hostapd_drv_sta_disassoc(hapd, addr, WLAN_REASON_DISASSOC_LOW_ACK); +- if (sta) +- ap_sta_disassociate(hapd, sta, WLAN_REASON_DISASSOC_LOW_ACK); ++ ap_sta_disassociate(hapd, sta, WLAN_REASON_DISASSOC_LOW_ACK); + } + + ++void hostapd_event_sta_opmode_changed(struct hostapd_data *hapd, const u8 *addr, ++ enum smps_mode smps_mode, ++ enum chan_width chan_width, u8 rx_nss) ++{ ++ struct sta_info *sta = ap_get_sta(hapd, addr); ++ const char *txt; ++ ++ if (!sta) ++ return; ++ ++ switch (smps_mode) { ++ case SMPS_AUTOMATIC: ++ txt = "automatic"; ++ break; ++ case SMPS_OFF: ++ txt = "off"; ++ break; ++ case SMPS_DYNAMIC: ++ txt = "dynamic"; ++ break; ++ case SMPS_STATIC: ++ txt = "static"; ++ break; ++ default: ++ txt = NULL; ++ break; ++ } ++ if (txt) { ++ wpa_msg(hapd->msg_ctx, MSG_INFO, STA_OPMODE_SMPS_MODE_CHANGED ++ MACSTR " %s", MAC2STR(addr), txt); ++ } ++ ++ switch (chan_width) { ++ case CHAN_WIDTH_20_NOHT: ++ txt = "20(no-HT)"; ++ break; ++ case CHAN_WIDTH_20: ++ txt = "20"; ++ break; ++ case CHAN_WIDTH_40: ++ txt = "40"; ++ break; ++ case CHAN_WIDTH_80: ++ txt = "80"; ++ break; ++ case CHAN_WIDTH_80P80: ++ txt = "80+80"; ++ break; ++ case CHAN_WIDTH_160: ++ txt = "160"; ++ break; ++ default: ++ txt = NULL; ++ break; ++ } ++ if (txt) { ++ wpa_msg(hapd->msg_ctx, MSG_INFO, STA_OPMODE_MAX_BW_CHANGED ++ MACSTR " %s", MAC2STR(addr), txt); ++ } ++ ++ if (rx_nss != 0xff) { ++ wpa_msg(hapd->msg_ctx, MSG_INFO, STA_OPMODE_N_SS_CHANGED ++ MACSTR " %d", MAC2STR(addr), rx_nss); ++ } ++} ++ ++ + void hostapd_event_ch_switch(struct hostapd_data *hapd, int freq, int ht, + int offset, int width, int cf1, int cf2) + { ++ /* TODO: If OCV is enabled deauth STAs that don't perform a SA Query */ ++ + #ifdef NEED_AP_MLME +- int channel, chwidth, seg0_idx = 0, seg1_idx = 0, is_dfs; ++ int channel, chwidth, is_dfs; ++ u8 seg0_idx = 0, seg1_idx = 0; ++ size_t i; + + hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_INFO, +- "driver had channel switch: freq=%d, ht=%d, offset=%d, width=%d (%s), cf1=%d, cf2=%d", +- freq, ht, offset, width, channel_width_to_string(width), +- cf1, cf2); ++ "driver had channel switch: freq=%d, ht=%d, vht_ch=0x%x, offset=%d, width=%d (%s), cf1=%d, cf2=%d", ++ freq, ht, hapd->iconf->ch_switch_vht_config, offset, ++ width, channel_width_to_string(width), cf1, cf2); + + hapd->iface->freq = freq; + +@@ -491,21 +823,33 @@ + seg1_idx = (cf2 - 5000) / 5; + break; + default: +- seg0_idx = hostapd_hw_get_channel(hapd, cf1); +- seg1_idx = hostapd_hw_get_channel(hapd, cf2); ++ ieee80211_freq_to_chan(cf1, &seg0_idx); ++ ieee80211_freq_to_chan(cf2, &seg1_idx); + break; + } + + hapd->iconf->channel = channel; + hapd->iconf->ieee80211n = ht; +- if (!ht) ++ if (!ht) { + hapd->iconf->ieee80211ac = 0; ++ } else if (hapd->iconf->ch_switch_vht_config) { ++ /* CHAN_SWITCH VHT config */ ++ if (hapd->iconf->ch_switch_vht_config & ++ CH_SWITCH_VHT_ENABLED) ++ hapd->iconf->ieee80211ac = 1; ++ else if (hapd->iconf->ch_switch_vht_config & ++ CH_SWITCH_VHT_DISABLED) ++ hapd->iconf->ieee80211ac = 0; ++ } ++ hapd->iconf->ch_switch_vht_config = 0; ++ + hapd->iconf->secondary_channel = offset; + hapd->iconf->vht_oper_chwidth = chwidth; + hapd->iconf->vht_oper_centr_freq_seg0_idx = seg0_idx; + hapd->iconf->vht_oper_centr_freq_seg1_idx = seg1_idx; + +- is_dfs = ieee80211_is_dfs(freq); ++ is_dfs = ieee80211_is_dfs(freq, hapd->iface->hw_features, ++ hapd->iface->num_hw_features); + + if (hapd->csa_in_progress && + freq == hapd->cs_freq_params.freq) { +@@ -518,6 +862,9 @@ + wpa_msg(hapd->msg_ctx, MSG_INFO, AP_CSA_FINISHED + "freq=%d dfs=%d", freq, is_dfs); + } ++ ++ for (i = 0; i < hapd->iface->num_bss; i++) ++ hostapd_neighbor_set_own_report(hapd->iface->bss[i]); + #endif /* NEED_AP_MLME */ + } + +@@ -539,10 +886,11 @@ + + + #ifdef CONFIG_ACS +-static void hostapd_acs_channel_selected(struct hostapd_data *hapd, +- struct acs_selected_channels *acs_res) ++void hostapd_acs_channel_selected(struct hostapd_data *hapd, ++ struct acs_selected_channels *acs_res) + { + int ret, i; ++ int err = 0; + + if (hapd->iconf->channel) { + wpa_printf(MSG_INFO, "ACS: Channel was already set to %d", +@@ -564,7 +912,8 @@ + hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_WARNING, + "driver selected to bad hw_mode"); +- return; ++ err = 1; ++ goto out; + } + } + +@@ -574,7 +923,8 @@ + hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_WARNING, + "driver switched to bad channel"); +- return; ++ err = 1; ++ goto out; + } + + hapd->iconf->channel = acs_res->pri_channel; +@@ -588,7 +938,8 @@ + hapd->iconf->secondary_channel = 1; + else { + wpa_printf(MSG_ERROR, "Invalid secondary channel!"); +- return; ++ err = 1; ++ goto out; + } + + if (hapd->iface->conf->ieee80211ac) { +@@ -617,7 +968,8 @@ + } + } + +- ret = hostapd_acs_completed(hapd->iface, 0); ++out: ++ ret = hostapd_acs_completed(hapd->iface, err); + if (ret) { + wpa_printf(MSG_ERROR, + "ACS: Possibly channel configuration is invalid"); +@@ -651,7 +1003,7 @@ + + #ifdef HOSTAPD + +-#ifdef CONFIG_IEEE80211R ++#ifdef CONFIG_IEEE80211R_AP + static void hostapd_notify_auth_ft_finish(void *ctx, const u8 *dst, + const u8 *bssid, + u16 auth_transaction, u16 status, +@@ -670,9 +1022,35 @@ + + hostapd_sta_auth(hapd, dst, auth_transaction, status, ies, ies_len); + } +-#endif /* CONFIG_IEEE80211R */ ++#endif /* CONFIG_IEEE80211R_AP */ + + ++#ifdef CONFIG_FILS ++static void hostapd_notify_auth_fils_finish(struct hostapd_data *hapd, ++ struct sta_info *sta, u16 resp, ++ struct wpabuf *data, int pub) ++{ ++ if (resp == WLAN_STATUS_SUCCESS) { ++ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, ++ HOSTAPD_LEVEL_DEBUG, "authentication OK (FILS)"); ++ sta->flags |= WLAN_STA_AUTH; ++ wpa_auth_sm_event(sta->wpa_sm, WPA_AUTH); ++ sta->auth_alg = WLAN_AUTH_FILS_SK; ++ mlme_authenticate_indication(hapd, sta); ++ } else { ++ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, ++ HOSTAPD_LEVEL_DEBUG, ++ "authentication failed (FILS)"); ++ } ++ ++ hostapd_sta_auth(hapd, sta->addr, 2, resp, ++ data ? wpabuf_head(data) : NULL, ++ data ? wpabuf_len(data) : 0); ++ wpabuf_free(data); ++} ++#endif /* CONFIG_FILS */ ++ ++ + static void hostapd_notif_auth(struct hostapd_data *hapd, + struct auth_info *rx_auth) + { +@@ -691,7 +1069,7 @@ + } + sta->flags &= ~WLAN_STA_PREAUTH; + ieee802_1x_notify_pre_auth(sta->eapol_sm, 0); +-#ifdef CONFIG_IEEE80211R ++#ifdef CONFIG_IEEE80211R_AP + if (rx_auth->auth_type == WLAN_AUTH_FT && hapd->wpa_auth) { + sta->auth_alg = WLAN_AUTH_FT; + if (sta->wpa_sm == NULL) +@@ -709,7 +1087,19 @@ + hostapd_notify_auth_ft_finish, hapd); + return; + } +-#endif /* CONFIG_IEEE80211R */ ++#endif /* CONFIG_IEEE80211R_AP */ ++ ++#ifdef CONFIG_FILS ++ if (rx_auth->auth_type == WLAN_AUTH_FILS_SK) { ++ sta->auth_alg = WLAN_AUTH_FILS_SK; ++ handle_auth_fils(hapd, sta, rx_auth->ies, rx_auth->ies_len, ++ rx_auth->auth_type, rx_auth->auth_transaction, ++ rx_auth->status_code, ++ hostapd_notify_auth_fils_finish); ++ return; ++ } ++#endif /* CONFIG_FILS */ ++ + fail: + hostapd_sta_auth(hapd, rx_auth->peer, rx_auth->auth_transaction + 1, + status, resp_ies, resp_ies_len); +@@ -716,6 +1106,7 @@ + } + + ++#ifndef NEED_AP_MLME + static void hostapd_action_rx(struct hostapd_data *hapd, + struct rx_mgmt *drv_mgmt) + { +@@ -723,11 +1114,12 @@ + struct sta_info *sta; + size_t plen __maybe_unused; + u16 fc; ++ u8 *action __maybe_unused; + +- if (drv_mgmt->frame_len < 24 + 1) ++ if (drv_mgmt->frame_len < IEEE80211_HDRLEN + 2 + 1) + return; + +- plen = drv_mgmt->frame_len - 24 - 1; ++ plen = drv_mgmt->frame_len - IEEE80211_HDRLEN; + + mgmt = (struct ieee80211_mgmt *) drv_mgmt->frame; + fc = le_to_host16(mgmt->frame_control); +@@ -734,8 +1126,11 @@ + if (WLAN_FC_GET_STYPE(fc) != WLAN_FC_STYPE_ACTION) + return; /* handled by the driver */ + +- wpa_printf(MSG_DEBUG, "RX_ACTION cat %d action plen %d", +- mgmt->u.action.category, (int) plen); ++ action = (u8 *) &mgmt->u.action.u; ++ wpa_printf(MSG_DEBUG, "RX_ACTION category %u action %u sa " MACSTR ++ " da " MACSTR " plen %d", ++ mgmt->u.action.category, *action, ++ MAC2STR(mgmt->sa), MAC2STR(mgmt->da), (int) plen); + + sta = ap_get_sta(hapd, mgmt->sa); + if (sta == NULL) { +@@ -742,26 +1137,24 @@ + wpa_printf(MSG_DEBUG, "%s: station not found", __func__); + return; + } +-#ifdef CONFIG_IEEE80211R ++#ifdef CONFIG_IEEE80211R_AP + if (mgmt->u.action.category == WLAN_ACTION_FT) { +- const u8 *payload = drv_mgmt->frame + 24 + 1; +- +- wpa_ft_action_rx(sta->wpa_sm, payload, plen); ++ wpa_ft_action_rx(sta->wpa_sm, (u8 *) &mgmt->u.action, plen); ++ return; + } +-#endif /* CONFIG_IEEE80211R */ ++#endif /* CONFIG_IEEE80211R_AP */ + #ifdef CONFIG_IEEE80211W +- if (mgmt->u.action.category == WLAN_ACTION_SA_QUERY && plen >= 4) { +- ieee802_11_sa_query_action( +- hapd, mgmt->sa, +- mgmt->u.action.u.sa_query_resp.action, +- mgmt->u.action.u.sa_query_resp.trans_id); ++ if (mgmt->u.action.category == WLAN_ACTION_SA_QUERY) { ++ ieee802_11_sa_query_action(hapd, mgmt, drv_mgmt->frame_len); ++ return; + } + #endif /* CONFIG_IEEE80211W */ +-#ifdef CONFIG_WNM ++#ifdef CONFIG_WNM_AP + if (mgmt->u.action.category == WLAN_ACTION_WNM) { + ieee802_11_rx_wnm_action_ap(hapd, mgmt, drv_mgmt->frame_len); ++ return; + } +-#endif /* CONFIG_WNM */ ++#endif /* CONFIG_WNM_AP */ + #ifdef CONFIG_FST + if (mgmt->u.action.category == WLAN_ACTION_FST && hapd->iface->fst) { + fst_rx_action(hapd->iface->fst, mgmt, drv_mgmt->frame_len); +@@ -768,8 +1161,25 @@ + return; + } + #endif /* CONFIG_FST */ ++#ifdef CONFIG_DPP ++ if (plen >= 2 + 4 && ++ mgmt->u.action.u.vs_public_action.action == ++ WLAN_PA_VENDOR_SPECIFIC && ++ WPA_GET_BE24(mgmt->u.action.u.vs_public_action.oui) == ++ OUI_WFA && ++ mgmt->u.action.u.vs_public_action.variable[0] == ++ DPP_OUI_TYPE) { ++ const u8 *pos, *end; + ++ pos = mgmt->u.action.u.vs_public_action.oui; ++ end = drv_mgmt->frame + drv_mgmt->frame_len; ++ hostapd_dpp_rx_action(hapd, mgmt->sa, pos, end - pos, ++ drv_mgmt->freq); ++ return; ++ } ++#endif /* CONFIG_DPP */ + } ++#endif /* NEED_AP_MLME */ + + + #ifdef NEED_AP_MLME +@@ -852,6 +1262,7 @@ + } + + os_memset(&fi, 0, sizeof(fi)); ++ fi.freq = rx_mgmt->freq; + fi.datarate = rx_mgmt->datarate; + fi.ssi_signal = rx_mgmt->ssi_signal; + +@@ -884,11 +1295,24 @@ + size_t len, u16 stype, int ok) + { + struct ieee80211_hdr *hdr; ++ struct hostapd_data *orig_hapd = hapd; + + hdr = (struct ieee80211_hdr *) buf; + hapd = get_hapd_bssid(hapd->iface, get_hdr_bssid(hdr, len)); +- if (hapd == NULL || hapd == HAPD_BROADCAST) ++ if (!hapd) + return; ++ if (hapd == HAPD_BROADCAST) { ++ if (stype != WLAN_FC_STYPE_ACTION || len <= 25 || ++ buf[24] != WLAN_ACTION_PUBLIC) ++ return; ++ hapd = get_hapd_bssid(orig_hapd->iface, hdr->addr2); ++ if (!hapd || hapd == HAPD_BROADCAST) ++ return; ++ /* ++ * Allow processing of TX status for a Public Action frame that ++ * used wildcard BBSID. ++ */ ++ } + ieee802_11_mgmt_cb(hapd, buf, len, stype, ok); + } + +@@ -935,7 +1359,9 @@ + ieee802_1x_receive(hapd, src, data, data_len); + } + ++#endif /* HOSTAPD */ + ++ + static struct hostapd_channel_data * hostapd_get_mode_channel( + struct hostapd_iface *iface, unsigned int freq) + { +@@ -944,8 +1370,6 @@ + + for (i = 0; i < iface->current_mode->num_channels; i++) { + chan = &iface->current_mode->channels[i]; +- if (!chan) +- return NULL; + if ((unsigned int) chan->freq == freq) + return chan; + } +@@ -1009,10 +1433,9 @@ + } + + +-static void hostapd_event_get_survey(struct hostapd_data *hapd, +- struct survey_results *survey_results) ++void hostapd_event_get_survey(struct hostapd_iface *iface, ++ struct survey_results *survey_results) + { +- struct hostapd_iface *iface = hapd->iface; + struct freq_survey *survey, *tmp; + struct hostapd_channel_data *chan; + +@@ -1044,6 +1467,7 @@ + } + + ++#ifdef HOSTAPD + #ifdef NEED_AP_MLME + + static void hostapd_event_iface_unavailable(struct hostapd_data *hapd) +@@ -1070,6 +1494,16 @@ + } + + ++static void hostapd_event_dfs_pre_cac_expired(struct hostapd_data *hapd, ++ struct dfs_event *radar) ++{ ++ wpa_printf(MSG_DEBUG, "DFS Pre-CAC expired on %d MHz", radar->freq); ++ hostapd_dfs_pre_cac_expired(hapd->iface, radar->freq, radar->ht_enabled, ++ radar->chan_offset, radar->chan_width, ++ radar->cf1, radar->cf2); ++} ++ ++ + static void hostapd_event_dfs_cac_finished(struct hostapd_data *hapd, + struct dfs_event *radar) + { +@@ -1112,6 +1546,28 @@ + #endif /* NEED_AP_MLME */ + + ++static void hostapd_event_wds_sta_interface_status(struct hostapd_data *hapd, ++ int istatus, ++ const char *ifname, ++ const u8 *addr) ++{ ++ struct sta_info *sta = ap_get_sta(hapd, addr); ++ ++ if (sta) { ++ os_free(sta->ifname_wds); ++ if (istatus == INTERFACE_ADDED) ++ sta->ifname_wds = os_strdup(ifname); ++ else ++ sta->ifname_wds = NULL; ++ } ++ ++ wpa_msg(hapd->msg_ctx, MSG_INFO, "%sifname=%s sta_addr=" MACSTR, ++ istatus == INTERFACE_ADDED ? ++ WDS_STA_INTERFACE_ADDED : WDS_STA_INTERFACE_REMOVED, ++ ifname, MAC2STR(addr)); ++} ++ ++ + void wpa_supplicant_event(void *ctx, enum wpa_event_type event, + union wpa_event_data *data) + { +@@ -1185,10 +1641,10 @@ + if (!data->rx_mgmt.frame) + break; + #ifdef NEED_AP_MLME +- if (hostapd_mgmt_rx(hapd, &data->rx_mgmt) > 0) +- break; ++ hostapd_mgmt_rx(hapd, &data->rx_mgmt); ++#else /* NEED_AP_MLME */ ++ hostapd_action_rx(hapd, &data->rx_mgmt); + #endif /* NEED_AP_MLME */ +- hostapd_action_rx(hapd, &data->rx_mgmt); + break; + case EVENT_RX_PROBE_REQ: + if (data->rx_probe_req.sa == NULL || +@@ -1251,7 +1707,7 @@ + data->connect_failed_reason.code); + break; + case EVENT_SURVEY: +- hostapd_event_get_survey(hapd, &data->survey_results); ++ hostapd_event_get_survey(hapd->iface, &data->survey_results); + break; + #ifdef NEED_AP_MLME + case EVENT_INTERFACE_UNAVAILABLE: +@@ -1262,6 +1718,11 @@ + break; + hostapd_event_dfs_radar_detected(hapd, &data->dfs_event); + break; ++ case EVENT_DFS_PRE_CAC_EXPIRED: ++ if (!data) ++ break; ++ hostapd_event_dfs_pre_cac_expired(hapd, &data->dfs_event); ++ break; + case EVENT_DFS_CAC_FINISHED: + if (!data) + break; +@@ -1299,9 +1760,17 @@ + * Try to re-enable interface if the driver stopped it + * when the interface got disabled. + */ +- wpa_auth_reconfig_group_keys(hapd->wpa_auth); ++ if (hapd->wpa_auth) ++ wpa_auth_reconfig_group_keys(hapd->wpa_auth); ++ else ++ hostapd_reconfig_encryption(hapd); + hapd->reenable_beacon = 1; + ieee802_11_set_beacon(hapd); ++#ifdef NEED_AP_MLME ++ } else if (hapd->disabled && hapd->iface->cac_started) { ++ wpa_printf(MSG_DEBUG, "DFS: restarting pending CAC"); ++ hostapd_handle_dfs(hapd->iface); ++#endif /* NEED_AP_MLME */ + } + break; + case EVENT_INTERFACE_DISABLED: +@@ -1315,6 +1784,18 @@ + &data->acs_selected_channels); + break; + #endif /* CONFIG_ACS */ ++ case EVENT_STATION_OPMODE_CHANGED: ++ hostapd_event_sta_opmode_changed(hapd, data->sta_opmode.addr, ++ data->sta_opmode.smps_mode, ++ data->sta_opmode.chan_width, ++ data->sta_opmode.rx_nss); ++ break; ++ case EVENT_WDS_STA_INTERFACE_STATUS: ++ hostapd_event_wds_sta_interface_status( ++ hapd, data->wds_sta_interface.istatus, ++ data->wds_sta_interface.ifname, ++ data->wds_sta_interface.sta_addr); ++ break; + default: + wpa_printf(MSG_DEBUG, "Unknown event %d", event); + break; +@@ -1321,4 +1802,31 @@ + } + } + ++ ++void wpa_supplicant_event_global(void *ctx, enum wpa_event_type event, ++ union wpa_event_data *data) ++{ ++ struct hapd_interfaces *interfaces = ctx; ++ struct hostapd_data *hapd; ++ ++ if (event != EVENT_INTERFACE_STATUS) ++ return; ++ ++ hapd = hostapd_get_iface(interfaces, data->interface_status.ifname); ++ if (hapd && hapd->driver && hapd->driver->get_ifindex && ++ hapd->drv_priv) { ++ unsigned int ifindex; ++ ++ ifindex = hapd->driver->get_ifindex(hapd->drv_priv); ++ if (ifindex != data->interface_status.ifindex) { ++ wpa_dbg(hapd->msg_ctx, MSG_DEBUG, ++ "interface status ifindex %d mismatch (%d)", ++ ifindex, data->interface_status.ifindex); ++ return; ++ } ++ } ++ if (hapd) ++ wpa_supplicant_event(hapd, event, data); ++} ++ + #endif /* HOSTAPD */ +--- contrib/wpa/src/ap/eap_user_db.c.orig ++++ contrib/wpa/src/ap/eap_user_db.c +@@ -91,6 +91,8 @@ + set_user_methods(user, argv[i]); + } else if (os_strcmp(col[i], "remediation") == 0 && argv[i]) { + user->remediation = strlen(argv[i]) > 0; ++ } else if (os_strcmp(col[i], "t_c_timestamp") == 0 && argv[i]) { ++ user->t_c_timestamp = strtol(argv[i], NULL, 10); + } + } + +@@ -137,6 +139,7 @@ + struct hostapd_eap_user *user = NULL; + char id_str[256], cmd[300]; + size_t i; ++ int res; + + if (identity_len >= sizeof(id_str)) { + wpa_printf(MSG_DEBUG, "%s: identity len too big: %d >= %d", +@@ -172,6 +175,7 @@ + if (hapd->tmp_eap_user.identity == NULL) + return NULL; + os_memcpy(hapd->tmp_eap_user.identity, identity, identity_len); ++ hapd->tmp_eap_user.identity_len = identity_len; + + if (sqlite3_open(hapd->conf->eap_user_sqlite, &db)) { + wpa_printf(MSG_INFO, "DB: Failed to open database %s: %s", +@@ -180,9 +184,12 @@ + return NULL; + } + +- os_snprintf(cmd, sizeof(cmd), +- "SELECT * FROM users WHERE identity='%s' AND phase2=%d;", +- id_str, phase2); ++ res = os_snprintf(cmd, sizeof(cmd), ++ "SELECT * FROM users WHERE identity='%s' AND phase2=%d;", ++ id_str, phase2); ++ if (os_snprintf_error(sizeof(cmd), res)) ++ goto fail; ++ + wpa_printf(MSG_DEBUG, "DB: %s", cmd); + if (sqlite3_exec(db, cmd, get_user_cb, &hapd->tmp_eap_user, NULL) != + SQLITE_OK) { +@@ -212,6 +219,7 @@ + } + } + ++fail: + sqlite3_close(db); + + return user; +--- contrib/wpa/src/ap/eth_p_oui.c.orig ++++ contrib/wpa/src/ap/eth_p_oui.c +@@ -0,0 +1,191 @@ ++/* ++ * hostapd / IEEE 802 OUI Extended EtherType 88-B7 ++ * Copyright (c) 2016, 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 "l2_packet/l2_packet.h" ++#include "hostapd.h" ++#include "eth_p_oui.h" ++ ++/* ++ * See IEEE Std 802-2014, Clause 9.2.4 for the definition of the OUI Extended ++ * EtherType 88-B7. This file implements this with OUI 00:13:74 and ++ * vendor-specific subtype 0x0001. ++ */ ++static const u8 global_oui[] = { 0x00, 0x13, 0x74, 0x00, 0x01 }; ++ ++struct eth_p_oui_iface { ++ struct dl_list list; ++ char ifname[IFNAMSIZ + 1]; ++ struct l2_packet_data *l2; ++ struct dl_list receiver; ++}; ++ ++struct eth_p_oui_ctx { ++ struct dl_list list; ++ struct eth_p_oui_iface *iface; ++ /* all data needed to deliver and unregister */ ++ u8 oui_suffix; /* last byte of OUI */ ++ void (*rx_callback)(void *ctx, const u8 *src_addr, ++ const u8 *dst_addr, u8 oui_suffix, ++ const u8 *buf, size_t len); ++ void *rx_callback_ctx; ++}; ++ ++ ++void eth_p_oui_deliver(struct eth_p_oui_ctx *ctx, const u8 *src_addr, ++ const u8 *dst_addr, const u8 *buf, size_t len) ++{ ++ ctx->rx_callback(ctx->rx_callback_ctx, src_addr, dst_addr, ++ ctx->oui_suffix, buf, len); ++} ++ ++ ++static void eth_p_rx(void *ctx, const u8 *src_addr, const u8 *buf, size_t len) ++{ ++ struct eth_p_oui_iface *iface = ctx; ++ struct eth_p_oui_ctx *receiver; ++ const struct l2_ethhdr *ethhdr; ++ ++ if (len < sizeof(*ethhdr) + sizeof(global_oui) + 1) { ++ /* too short packet */ ++ return; ++ } ++ ++ ethhdr = (struct l2_ethhdr *) buf; ++ /* trim eth_hdr from buf and len */ ++ buf += sizeof(*ethhdr); ++ len -= sizeof(*ethhdr); ++ ++ /* verify OUI and vendor-specific subtype match */ ++ if (os_memcmp(buf, global_oui, sizeof(global_oui)) != 0) ++ return; ++ buf += sizeof(global_oui); ++ len -= sizeof(global_oui); ++ ++ dl_list_for_each(receiver, &iface->receiver, ++ struct eth_p_oui_ctx, list) { ++ if (buf[0] != receiver->oui_suffix) ++ continue; ++ ++ eth_p_oui_deliver(receiver, ethhdr->h_source, ethhdr->h_dest, ++ buf + 1, len - 1); ++ } ++} ++ ++ ++struct eth_p_oui_ctx * ++eth_p_oui_register(struct hostapd_data *hapd, const char *ifname, u8 oui_suffix, ++ void (*rx_callback)(void *ctx, const u8 *src_addr, ++ const u8 *dst_addr, u8 oui_suffix, ++ const u8 *buf, size_t len), ++ void *rx_callback_ctx) ++{ ++ struct eth_p_oui_iface *iface; ++ struct eth_p_oui_ctx *receiver; ++ int found = 0; ++ struct hapd_interfaces *interfaces; ++ ++ receiver = os_zalloc(sizeof(*receiver)); ++ if (!receiver) ++ goto err; ++ ++ receiver->oui_suffix = oui_suffix; ++ receiver->rx_callback = rx_callback; ++ receiver->rx_callback_ctx = rx_callback_ctx; ++ ++ interfaces = hapd->iface->interfaces; ++ ++ dl_list_for_each(iface, &interfaces->eth_p_oui, struct eth_p_oui_iface, ++ list) { ++ if (os_strcmp(iface->ifname, ifname) != 0) ++ continue; ++ found = 1; ++ break; ++ } ++ ++ if (!found) { ++ iface = os_zalloc(sizeof(*iface)); ++ if (!iface) ++ goto err; ++ ++ os_strlcpy(iface->ifname, ifname, sizeof(iface->ifname)); ++ iface->l2 = l2_packet_init(ifname, NULL, ETH_P_OUI, eth_p_rx, ++ iface, 1); ++ if (!iface->l2) { ++ os_free(iface); ++ goto err; ++ } ++ dl_list_init(&iface->receiver); ++ ++ dl_list_add_tail(&interfaces->eth_p_oui, &iface->list); ++ } ++ ++ dl_list_add_tail(&iface->receiver, &receiver->list); ++ receiver->iface = iface; ++ ++ return receiver; ++err: ++ os_free(receiver); ++ return NULL; ++} ++ ++ ++void eth_p_oui_unregister(struct eth_p_oui_ctx *ctx) ++{ ++ struct eth_p_oui_iface *iface; ++ ++ if (!ctx) ++ return; ++ ++ iface = ctx->iface; ++ ++ dl_list_del(&ctx->list); ++ os_free(ctx); ++ ++ if (dl_list_empty(&iface->receiver)) { ++ dl_list_del(&iface->list); ++ l2_packet_deinit(iface->l2); ++ os_free(iface); ++ } ++} ++ ++ ++int eth_p_oui_send(struct eth_p_oui_ctx *ctx, const u8 *src_addr, ++ const u8 *dst_addr, const u8 *buf, size_t len) ++{ ++ struct eth_p_oui_iface *iface = ctx->iface; ++ u8 *packet, *p; ++ size_t packet_len; ++ int ret; ++ struct l2_ethhdr *ethhdr; ++ ++ packet_len = sizeof(*ethhdr) + sizeof(global_oui) + 1 + len; ++ packet = os_zalloc(packet_len); ++ if (!packet) ++ return -1; ++ p = packet; ++ ++ ethhdr = (struct l2_ethhdr *) packet; ++ os_memcpy(ethhdr->h_source, src_addr, ETH_ALEN); ++ os_memcpy(ethhdr->h_dest, dst_addr, ETH_ALEN); ++ ethhdr->h_proto = host_to_be16(ETH_P_OUI); ++ p += sizeof(*ethhdr); ++ ++ os_memcpy(p, global_oui, sizeof(global_oui)); ++ p[sizeof(global_oui)] = ctx->oui_suffix; ++ p += sizeof(global_oui) + 1; ++ ++ os_memcpy(p, buf, len); ++ ++ ret = l2_packet_send(iface->l2, NULL, 0, packet, packet_len); ++ os_free(packet); ++ return ret; ++} +--- contrib/wpa/src/ap/eth_p_oui.h.orig ++++ contrib/wpa/src/ap/eth_p_oui.h +@@ -0,0 +1,28 @@ ++/* ++ * hostapd / IEEE 802 OUI Extended Ethertype ++ * Copyright (c) 2016, Jouni Malinen ++ * ++ * This software may be distributed under the terms of the BSD license. ++ * See README for more details. ++ */ ++ ++#ifndef ETH_P_OUI_H ++#define ETH_P_OUI_H ++ ++struct eth_p_oui_ctx; ++struct hostapd_data; ++ ++/* rx_callback only gets payload after OUI passed as buf */ ++struct eth_p_oui_ctx * ++eth_p_oui_register(struct hostapd_data *hapd, const char *ifname, u8 oui_suffix, ++ void (*rx_callback)(void *ctx, const u8 *src_addr, ++ const u8 *dst_addr, u8 oui_suffix, ++ const u8 *buf, size_t len), ++ void *rx_callback_ctx); ++void eth_p_oui_unregister(struct eth_p_oui_ctx *eth_p_oui); ++int eth_p_oui_send(struct eth_p_oui_ctx *ctx, const u8 *src_addr, ++ const u8 *dst_addr, const u8 *buf, size_t len); ++void eth_p_oui_deliver(struct eth_p_oui_ctx *ctx, const u8 *src_addr, ++ const u8 *dst_addr, const u8 *buf, size_t len); ++ ++#endif /* ETH_P_OUI_H */ +--- contrib/wpa/src/ap/fils_hlp.c.orig ++++ contrib/wpa/src/ap/fils_hlp.c +@@ -0,0 +1,654 @@ ++/* ++ * FILS HLP request processing ++ * Copyright (c) 2017, 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 "utils/eloop.h" ++#include "common/dhcp.h" ++#include "hostapd.h" ++#include "sta_info.h" ++#include "ieee802_11.h" ++#include "fils_hlp.h" ++ ++ ++static be16 ip_checksum(const void *buf, size_t len) ++{ ++ u32 sum = 0; ++ const u16 *pos; ++ ++ for (pos = buf; len >= 2; len -= 2) ++ sum += ntohs(*pos++); ++ if (len) ++ sum += ntohs(*pos << 8); ++ ++ sum = (sum >> 16) + (sum & 0xffff); ++ sum += sum >> 16; ++ return htons(~sum); ++} ++ ++ ++static int fils_dhcp_request(struct hostapd_data *hapd, struct sta_info *sta, ++ struct dhcp_data *dhcpoffer, u8 *dhcpofferend) ++{ ++ u8 *pos, *end; ++ struct dhcp_data *dhcp; ++ struct sockaddr_in addr; ++ ssize_t res; ++ const u8 *server_id = NULL; ++ ++ if (!sta->hlp_dhcp_discover) { ++ wpa_printf(MSG_DEBUG, ++ "FILS: No pending HLP DHCPDISCOVER available"); ++ return -1; ++ } ++ ++ /* Convert to DHCPREQUEST, remove rapid commit option, replace requested ++ * IP address option with yiaddr. */ ++ pos = wpabuf_mhead(sta->hlp_dhcp_discover); ++ end = pos + wpabuf_len(sta->hlp_dhcp_discover); ++ dhcp = (struct dhcp_data *) pos; ++ pos = (u8 *) (dhcp + 1); ++ pos += 4; /* skip magic */ ++ while (pos < end && *pos != DHCP_OPT_END) { ++ u8 opt, olen; ++ ++ opt = *pos++; ++ if (opt == DHCP_OPT_PAD) ++ continue; ++ if (pos >= end) ++ break; ++ olen = *pos++; ++ if (olen > end - pos) ++ break; ++ ++ switch (opt) { ++ case DHCP_OPT_MSG_TYPE: ++ if (olen > 0) ++ *pos = DHCPREQUEST; ++ break; ++ case DHCP_OPT_RAPID_COMMIT: ++ case DHCP_OPT_REQUESTED_IP_ADDRESS: ++ case DHCP_OPT_SERVER_ID: ++ /* Remove option */ ++ pos -= 2; ++ os_memmove(pos, pos + 2 + olen, end - pos - 2 - olen); ++ end -= 2 + olen; ++ olen = 0; ++ break; ++ } ++ pos += olen; ++ } ++ if (pos >= end || *pos != DHCP_OPT_END) { ++ wpa_printf(MSG_DEBUG, "FILS: Could not update DHCPDISCOVER"); ++ return -1; ++ } ++ sta->hlp_dhcp_discover->used = pos - (u8 *) dhcp; ++ ++ /* Copy Server ID option from DHCPOFFER to DHCPREQUEST */ ++ pos = (u8 *) (dhcpoffer + 1); ++ end = dhcpofferend; ++ pos += 4; /* skip magic */ ++ while (pos < end && *pos != DHCP_OPT_END) { ++ u8 opt, olen; ++ ++ opt = *pos++; ++ if (opt == DHCP_OPT_PAD) ++ continue; ++ if (pos >= end) ++ break; ++ olen = *pos++; ++ if (olen > end - pos) ++ break; ++ ++ switch (opt) { ++ case DHCP_OPT_SERVER_ID: ++ server_id = pos - 2; ++ break; ++ } ++ pos += olen; ++ } ++ ++ if (wpabuf_resize(&sta->hlp_dhcp_discover, ++ 6 + 1 + (server_id ? 2 + server_id[1] : 0))) ++ return -1; ++ if (server_id) ++ wpabuf_put_data(sta->hlp_dhcp_discover, server_id, ++ 2 + server_id[1]); ++ wpabuf_put_u8(sta->hlp_dhcp_discover, DHCP_OPT_REQUESTED_IP_ADDRESS); ++ wpabuf_put_u8(sta->hlp_dhcp_discover, 4); ++ wpabuf_put_data(sta->hlp_dhcp_discover, &dhcpoffer->your_ip, 4); ++ wpabuf_put_u8(sta->hlp_dhcp_discover, DHCP_OPT_END); ++ ++ os_memset(&addr, 0, sizeof(addr)); ++ addr.sin_family = AF_INET; ++ addr.sin_addr.s_addr = hapd->conf->dhcp_server.u.v4.s_addr; ++ addr.sin_port = htons(hapd->conf->dhcp_server_port); ++ res = sendto(hapd->dhcp_sock, wpabuf_head(sta->hlp_dhcp_discover), ++ wpabuf_len(sta->hlp_dhcp_discover), 0, ++ (const struct sockaddr *) &addr, sizeof(addr)); ++ if (res < 0) { ++ wpa_printf(MSG_ERROR, "FILS: DHCP sendto failed: %s", ++ strerror(errno)); ++ return -1; ++ } ++ wpa_printf(MSG_DEBUG, ++ "FILS: Acting as DHCP rapid commit proxy for %s:%d", ++ inet_ntoa(addr.sin_addr), ntohs(addr.sin_port)); ++ wpabuf_free(sta->hlp_dhcp_discover); ++ sta->hlp_dhcp_discover = NULL; ++ sta->fils_dhcp_rapid_commit_proxy = 1; ++ return 0; ++} ++ ++ ++static void fils_dhcp_handler(int sd, void *eloop_ctx, void *sock_ctx) ++{ ++ struct hostapd_data *hapd = sock_ctx; ++ struct sta_info *sta; ++ u8 buf[1500], *pos, *end, *end_opt = NULL; ++ struct dhcp_data *dhcp; ++ struct sockaddr_in addr; ++ socklen_t addr_len; ++ ssize_t res; ++ u8 msgtype = 0; ++ int rapid_commit = 0; ++ struct iphdr *iph; ++ struct udphdr *udph; ++ struct wpabuf *resp; ++ const u8 *rpos; ++ size_t left, len; ++ ++ addr_len = sizeof(addr); ++ res = recvfrom(sd, buf, sizeof(buf), 0, ++ (struct sockaddr *) &addr, &addr_len); ++ if (res < 0) { ++ wpa_printf(MSG_DEBUG, "FILS: DHCP read failed: %s", ++ strerror(errno)); ++ return; ++ } ++ wpa_printf(MSG_DEBUG, "FILS: DHCP response from server %s:%d (len=%d)", ++ inet_ntoa(addr.sin_addr), ntohs(addr.sin_port), (int) res); ++ wpa_hexdump(MSG_MSGDUMP, "FILS: HLP - DHCP server response", buf, res); ++ if ((size_t) res < sizeof(*dhcp)) ++ return; ++ dhcp = (struct dhcp_data *) buf; ++ if (dhcp->op != 2) ++ return; /* Not a BOOTREPLY */ ++ if (dhcp->relay_ip != hapd->conf->own_ip_addr.u.v4.s_addr) { ++ wpa_printf(MSG_DEBUG, ++ "FILS: HLP - DHCP response to unknown relay address 0x%x", ++ dhcp->relay_ip); ++ return; ++ } ++ dhcp->relay_ip = 0; ++ pos = (u8 *) (dhcp + 1); ++ end = &buf[res]; ++ ++ if (end - pos < 4 || WPA_GET_BE32(pos) != DHCP_MAGIC) { ++ wpa_printf(MSG_DEBUG, "FILS: HLP - no DHCP magic in response"); ++ return; ++ } ++ pos += 4; ++ ++ wpa_hexdump(MSG_DEBUG, "FILS: HLP - DHCP options in response", ++ pos, end - pos); ++ while (pos < end && *pos != DHCP_OPT_END) { ++ u8 opt, olen; ++ ++ opt = *pos++; ++ if (opt == DHCP_OPT_PAD) ++ continue; ++ if (pos >= end) ++ break; ++ olen = *pos++; ++ if (olen > end - pos) ++ break; ++ ++ switch (opt) { ++ case DHCP_OPT_MSG_TYPE: ++ if (olen > 0) ++ msgtype = pos[0]; ++ break; ++ case DHCP_OPT_RAPID_COMMIT: ++ rapid_commit = 1; ++ break; ++ } ++ pos += olen; ++ } ++ if (pos < end && *pos == DHCP_OPT_END) ++ end_opt = pos; ++ ++ wpa_printf(MSG_DEBUG, ++ "FILS: HLP - DHCP message type %u (rapid_commit=%d hw_addr=" ++ MACSTR ")", ++ msgtype, rapid_commit, MAC2STR(dhcp->hw_addr)); ++ ++ sta = ap_get_sta(hapd, dhcp->hw_addr); ++ if (!sta || !sta->fils_pending_assoc_req) { ++ wpa_printf(MSG_DEBUG, ++ "FILS: No pending HLP DHCP exchange with hw_addr " ++ MACSTR, MAC2STR(dhcp->hw_addr)); ++ return; ++ } ++ ++ if (hapd->conf->dhcp_rapid_commit_proxy && msgtype == DHCPOFFER && ++ !rapid_commit) { ++ /* Use hostapd to take care of 4-message exchange and convert ++ * the final DHCPACK to rapid commit version. */ ++ if (fils_dhcp_request(hapd, sta, dhcp, end) == 0) ++ return; ++ /* failed, so send the server response as-is */ ++ } else if (msgtype != DHCPACK) { ++ wpa_printf(MSG_DEBUG, ++ "FILS: No DHCPACK available from the server and cannot do rapid commit proxying"); ++ } ++ ++ pos = buf; ++ resp = wpabuf_alloc(2 * ETH_ALEN + 6 + 2 + ++ sizeof(*iph) + sizeof(*udph) + (end - pos) + 2); ++ if (!resp) ++ return; ++ wpabuf_put_data(resp, sta->addr, ETH_ALEN); ++ wpabuf_put_data(resp, hapd->own_addr, ETH_ALEN); ++ wpabuf_put_data(resp, "\xaa\xaa\x03\x00\x00\x00", 6); ++ wpabuf_put_be16(resp, ETH_P_IP); ++ iph = wpabuf_put(resp, sizeof(*iph)); ++ iph->version = 4; ++ iph->ihl = sizeof(*iph) / 4; ++ iph->tot_len = htons(sizeof(*iph) + sizeof(*udph) + (end - pos)); ++ iph->ttl = 1; ++ iph->protocol = 17; /* UDP */ ++ iph->saddr = hapd->conf->dhcp_server.u.v4.s_addr; ++ iph->daddr = dhcp->client_ip; ++ iph->check = ip_checksum(iph, sizeof(*iph)); ++ udph = wpabuf_put(resp, sizeof(*udph)); ++ udph->uh_sport = htons(DHCP_SERVER_PORT); ++ udph->uh_dport = htons(DHCP_CLIENT_PORT); ++ udph->uh_ulen = htons(sizeof(*udph) + (end - pos)); ++ udph->uh_sum = htons(0x0000); /* TODO: calculate checksum */ ++ if (hapd->conf->dhcp_rapid_commit_proxy && msgtype == DHCPACK && ++ !rapid_commit && sta->fils_dhcp_rapid_commit_proxy && end_opt) { ++ /* Add rapid commit option */ ++ wpabuf_put_data(resp, pos, end_opt - pos); ++ wpabuf_put_u8(resp, DHCP_OPT_RAPID_COMMIT); ++ wpabuf_put_u8(resp, 0); ++ wpabuf_put_data(resp, end_opt, end - end_opt); ++ } else { ++ wpabuf_put_data(resp, pos, end - pos); ++ } ++ if (wpabuf_resize(&sta->fils_hlp_resp, wpabuf_len(resp) + ++ 2 * wpabuf_len(resp) / 255 + 100)) { ++ wpabuf_free(resp); ++ return; ++ } ++ ++ rpos = wpabuf_head(resp); ++ left = wpabuf_len(resp); ++ ++ wpabuf_put_u8(sta->fils_hlp_resp, WLAN_EID_EXTENSION); /* Element ID */ ++ if (left <= 254) ++ len = 1 + left; ++ else ++ len = 255; ++ wpabuf_put_u8(sta->fils_hlp_resp, len); /* Length */ ++ /* Element ID Extension */ ++ wpabuf_put_u8(sta->fils_hlp_resp, WLAN_EID_EXT_FILS_HLP_CONTAINER); ++ /* Destination MAC Address, Source MAC Address, HLP Packet. ++ * HLP Packet is in MSDU format (i.e., including the LLC/SNAP header ++ * when LPD is used). */ ++ wpabuf_put_data(sta->fils_hlp_resp, rpos, len - 1); ++ rpos += len - 1; ++ left -= len - 1; ++ while (left) { ++ wpabuf_put_u8(sta->fils_hlp_resp, WLAN_EID_FRAGMENT); ++ len = left > 255 ? 255 : left; ++ wpabuf_put_u8(sta->fils_hlp_resp, len); ++ wpabuf_put_data(sta->fils_hlp_resp, rpos, len); ++ rpos += len; ++ left -= len; ++ } ++ wpabuf_free(resp); ++ ++ if (sta->fils_drv_assoc_finish) ++ hostapd_notify_assoc_fils_finish(hapd, sta); ++ else ++ fils_hlp_finish_assoc(hapd, sta); ++} ++ ++ ++static int fils_process_hlp_dhcp(struct hostapd_data *hapd, ++ struct sta_info *sta, ++ const u8 *msg, size_t len) ++{ ++ const struct dhcp_data *dhcp; ++ struct wpabuf *dhcp_buf; ++ struct dhcp_data *dhcp_msg; ++ u8 msgtype = 0; ++ int rapid_commit = 0; ++ const u8 *pos = msg, *end; ++ struct sockaddr_in addr; ++ ssize_t res; ++ ++ if (len < sizeof(*dhcp)) ++ return 0; ++ dhcp = (const struct dhcp_data *) pos; ++ end = pos + len; ++ wpa_printf(MSG_DEBUG, ++ "FILS: HLP request DHCP: op=%u htype=%u hlen=%u hops=%u xid=0x%x", ++ dhcp->op, dhcp->htype, dhcp->hlen, dhcp->hops, ++ ntohl(dhcp->xid)); ++ pos += sizeof(*dhcp); ++ if (dhcp->op != 1) ++ return 0; /* Not a BOOTREQUEST */ ++ ++ if (end - pos < 4) ++ return 0; ++ if (WPA_GET_BE32(pos) != DHCP_MAGIC) { ++ wpa_printf(MSG_DEBUG, "FILS: HLP - no DHCP magic"); ++ return 0; ++ } ++ pos += 4; ++ ++ wpa_hexdump(MSG_DEBUG, "FILS: HLP - DHCP options", pos, end - pos); ++ while (pos < end && *pos != DHCP_OPT_END) { ++ u8 opt, olen; ++ ++ opt = *pos++; ++ if (opt == DHCP_OPT_PAD) ++ continue; ++ if (pos >= end) ++ break; ++ olen = *pos++; ++ if (olen > end - pos) ++ break; ++ ++ switch (opt) { ++ case DHCP_OPT_MSG_TYPE: ++ if (olen > 0) ++ msgtype = pos[0]; ++ break; ++ case DHCP_OPT_RAPID_COMMIT: ++ rapid_commit = 1; ++ break; ++ } ++ pos += olen; ++ } ++ ++ wpa_printf(MSG_DEBUG, "FILS: HLP - DHCP message type %u", msgtype); ++ if (msgtype != DHCPDISCOVER) ++ return 0; ++ ++ if (hapd->conf->dhcp_server.af != AF_INET || ++ hapd->conf->dhcp_server.u.v4.s_addr == 0) { ++ wpa_printf(MSG_DEBUG, ++ "FILS: HLP - no DHCPv4 server configured - drop request"); ++ return 0; ++ } ++ ++ if (hapd->conf->own_ip_addr.af != AF_INET || ++ hapd->conf->own_ip_addr.u.v4.s_addr == 0) { ++ wpa_printf(MSG_DEBUG, ++ "FILS: HLP - no IPv4 own_ip_addr configured - drop request"); ++ return 0; ++ } ++ ++ if (hapd->dhcp_sock < 0) { ++ int s; ++ ++ s = socket(AF_INET, SOCK_DGRAM, 0); ++ if (s < 0) { ++ wpa_printf(MSG_ERROR, ++ "FILS: Failed to open DHCP socket: %s", ++ strerror(errno)); ++ return 0; ++ } ++ ++ if (hapd->conf->dhcp_relay_port) { ++ os_memset(&addr, 0, sizeof(addr)); ++ addr.sin_family = AF_INET; ++ addr.sin_addr.s_addr = ++ hapd->conf->own_ip_addr.u.v4.s_addr; ++ addr.sin_port = htons(hapd->conf->dhcp_relay_port); ++ if (bind(s, (struct sockaddr *) &addr, sizeof(addr))) { ++ wpa_printf(MSG_ERROR, ++ "FILS: Failed to bind DHCP socket: %s", ++ strerror(errno)); ++ close(s); ++ return 0; ++ } ++ } ++ if (eloop_register_sock(s, EVENT_TYPE_READ, ++ fils_dhcp_handler, NULL, hapd)) { ++ close(s); ++ return 0; ++ } ++ ++ hapd->dhcp_sock = s; ++ } ++ ++ dhcp_buf = wpabuf_alloc(len); ++ if (!dhcp_buf) ++ return 0; ++ dhcp_msg = wpabuf_put(dhcp_buf, len); ++ os_memcpy(dhcp_msg, msg, len); ++ dhcp_msg->relay_ip = hapd->conf->own_ip_addr.u.v4.s_addr; ++ os_memset(&addr, 0, sizeof(addr)); ++ addr.sin_family = AF_INET; ++ addr.sin_addr.s_addr = hapd->conf->dhcp_server.u.v4.s_addr; ++ addr.sin_port = htons(hapd->conf->dhcp_server_port); ++ res = sendto(hapd->dhcp_sock, dhcp_msg, len, 0, ++ (const struct sockaddr *) &addr, sizeof(addr)); ++ if (res < 0) { ++ wpa_printf(MSG_ERROR, "FILS: DHCP sendto failed: %s", ++ strerror(errno)); ++ wpabuf_free(dhcp_buf); ++ /* Close the socket to try to recover from error */ ++ eloop_unregister_read_sock(hapd->dhcp_sock); ++ close(hapd->dhcp_sock); ++ hapd->dhcp_sock = -1; ++ return 0; ++ } ++ ++ wpa_printf(MSG_DEBUG, ++ "FILS: HLP relayed DHCP request to server %s:%d (rapid_commit=%d)", ++ inet_ntoa(addr.sin_addr), ntohs(addr.sin_port), ++ rapid_commit); ++ if (hapd->conf->dhcp_rapid_commit_proxy && rapid_commit) { ++ /* Store a copy of the DHCPDISCOVER for rapid commit proxying ++ * purposes if the server does not support the rapid commit ++ * option. */ ++ wpa_printf(MSG_DEBUG, ++ "FILS: Store DHCPDISCOVER for rapid commit proxy"); ++ wpabuf_free(sta->hlp_dhcp_discover); ++ sta->hlp_dhcp_discover = dhcp_buf; ++ } else { ++ wpabuf_free(dhcp_buf); ++ } ++ ++ return 1; ++} ++ ++ ++static int fils_process_hlp_udp(struct hostapd_data *hapd, ++ struct sta_info *sta, const u8 *dst, ++ const u8 *pos, size_t len) ++{ ++ const struct iphdr *iph; ++ const struct udphdr *udph; ++ u16 sport, dport, ulen; ++ ++ if (len < sizeof(*iph) + sizeof(*udph)) ++ return 0; ++ iph = (const struct iphdr *) pos; ++ udph = (const struct udphdr *) (iph + 1); ++ sport = ntohs(udph->uh_sport); ++ dport = ntohs(udph->uh_dport); ++ ulen = ntohs(udph->uh_ulen); ++ wpa_printf(MSG_DEBUG, ++ "FILS: HLP request UDP: sport=%u dport=%u ulen=%u sum=0x%x", ++ sport, dport, ulen, ntohs(udph->uh_sum)); ++ /* TODO: Check UDP checksum */ ++ if (ulen < sizeof(*udph) || ulen > len - sizeof(*iph)) ++ return 0; ++ ++ if (dport == DHCP_SERVER_PORT && sport == DHCP_CLIENT_PORT) { ++ return fils_process_hlp_dhcp(hapd, sta, (const u8 *) (udph + 1), ++ ulen - sizeof(*udph)); ++ } ++ ++ return 0; ++} ++ ++ ++static int fils_process_hlp_ip(struct hostapd_data *hapd, ++ struct sta_info *sta, const u8 *dst, ++ const u8 *pos, size_t len) ++{ ++ const struct iphdr *iph; ++ u16 tot_len; ++ ++ if (len < sizeof(*iph)) ++ return 0; ++ iph = (const struct iphdr *) pos; ++ if (ip_checksum(iph, sizeof(*iph)) != 0) { ++ wpa_printf(MSG_DEBUG, ++ "FILS: HLP request IPv4 packet had invalid header checksum - dropped"); ++ return 0; ++ } ++ tot_len = ntohs(iph->tot_len); ++ if (tot_len > len) ++ return 0; ++ wpa_printf(MSG_DEBUG, ++ "FILS: HLP request IPv4: saddr=%08x daddr=%08x protocol=%u", ++ iph->saddr, iph->daddr, iph->protocol); ++ switch (iph->protocol) { ++ case 17: ++ return fils_process_hlp_udp(hapd, sta, dst, pos, len); ++ } ++ ++ return 0; ++} ++ ++ ++static int fils_process_hlp_req(struct hostapd_data *hapd, ++ struct sta_info *sta, ++ const u8 *pos, size_t len) ++{ ++ const u8 *pkt, *end; ++ ++ wpa_printf(MSG_DEBUG, "FILS: HLP request from " MACSTR " (dst=" MACSTR ++ " src=" MACSTR " len=%u)", ++ MAC2STR(sta->addr), MAC2STR(pos), MAC2STR(pos + ETH_ALEN), ++ (unsigned int) len); ++ if (os_memcmp(sta->addr, pos + ETH_ALEN, ETH_ALEN) != 0) { ++ wpa_printf(MSG_DEBUG, ++ "FILS: Ignore HLP request with unexpected source address" ++ MACSTR, MAC2STR(pos + ETH_ALEN)); ++ return 0; ++ } ++ ++ end = pos + len; ++ pkt = pos + 2 * ETH_ALEN; ++ if (end - pkt >= 6 && ++ os_memcmp(pkt, "\xaa\xaa\x03\x00\x00\x00", 6) == 0) ++ pkt += 6; /* Remove SNAP/LLC header */ ++ wpa_hexdump(MSG_MSGDUMP, "FILS: HLP request packet", pkt, end - pkt); ++ ++ if (end - pkt < 2) ++ return 0; ++ ++ switch (WPA_GET_BE16(pkt)) { ++ case ETH_P_IP: ++ return fils_process_hlp_ip(hapd, sta, pos, pkt + 2, ++ end - pkt - 2); ++ } ++ ++ return 0; ++} ++ ++ ++int fils_process_hlp(struct hostapd_data *hapd, struct sta_info *sta, ++ const u8 *pos, int left) ++{ ++ const u8 *end = pos + left; ++ u8 *tmp, *tmp_pos; ++ int ret = 0; ++ ++ if (sta->fils_pending_assoc_req && ++ eloop_is_timeout_registered(fils_hlp_timeout, hapd, sta)) { ++ /* Do not process FILS HLP request again if the station ++ * retransmits (Re)Association Request frame before the previous ++ * HLP response has either been received or timed out. */ ++ wpa_printf(MSG_DEBUG, ++ "FILS: Do not relay another HLP request from " ++ MACSTR ++ " before processing of the already pending one has been completed", ++ MAC2STR(sta->addr)); ++ return 1; ++ } ++ ++ /* Old DHCPDISCOVER is not needed anymore, if it was still pending */ ++ wpabuf_free(sta->hlp_dhcp_discover); ++ sta->hlp_dhcp_discover = NULL; ++ sta->fils_dhcp_rapid_commit_proxy = 0; ++ ++ /* Check if there are any FILS HLP Container elements */ ++ while (end - pos >= 2) { ++ if (2 + pos[1] > end - pos) ++ return 0; ++ if (pos[0] == WLAN_EID_EXTENSION && ++ pos[1] >= 1 + 2 * ETH_ALEN && ++ pos[2] == WLAN_EID_EXT_FILS_HLP_CONTAINER) ++ break; ++ pos += 2 + pos[1]; ++ } ++ if (end - pos < 2) ++ return 0; /* No FILS HLP Container elements */ ++ ++ tmp = os_malloc(end - pos); ++ if (!tmp) ++ return 0; ++ ++ while (end - pos >= 2) { ++ if (2 + pos[1] > end - pos || ++ pos[0] != WLAN_EID_EXTENSION || ++ pos[1] < 1 + 2 * ETH_ALEN || ++ pos[2] != WLAN_EID_EXT_FILS_HLP_CONTAINER) ++ break; ++ tmp_pos = tmp; ++ os_memcpy(tmp_pos, pos + 3, pos[1] - 1); ++ tmp_pos += pos[1] - 1; ++ pos += 2 + pos[1]; ++ ++ /* Add possible fragments */ ++ while (end - pos >= 2 && pos[0] == WLAN_EID_FRAGMENT && ++ 2 + pos[1] <= end - pos) { ++ os_memcpy(tmp_pos, pos + 2, pos[1]); ++ tmp_pos += pos[1]; ++ pos += 2 + pos[1]; ++ } ++ ++ if (fils_process_hlp_req(hapd, sta, tmp, tmp_pos - tmp) > 0) ++ ret = 1; ++ } ++ ++ os_free(tmp); ++ ++ return ret; ++} ++ ++ ++void fils_hlp_deinit(struct hostapd_data *hapd) ++{ ++ if (hapd->dhcp_sock >= 0) { ++ eloop_unregister_read_sock(hapd->dhcp_sock); ++ close(hapd->dhcp_sock); ++ hapd->dhcp_sock = -1; ++ } ++} +--- contrib/wpa/src/ap/fils_hlp.h.orig ++++ contrib/wpa/src/ap/fils_hlp.h +@@ -0,0 +1,27 @@ ++/* ++ * FILS HLP request processing ++ * Copyright (c) 2017, Qualcomm Atheros, Inc. ++ * ++ * This software may be distributed under the terms of the BSD license. ++ * See README for more details. ++ */ ++ ++#ifndef FILS_HLP_H ++#define FILS_HLP_H ++ ++int fils_process_hlp(struct hostapd_data *hapd, struct sta_info *sta, ++ const u8 *pos, int left); ++ ++#ifdef CONFIG_FILS ++ ++void fils_hlp_deinit(struct hostapd_data *hapd); ++ ++#else /* CONFIG_FILS */ ++ ++static inline void fils_hlp_deinit(struct hostapd_data *hapd) ++{ ++} ++ ++#endif /* CONFIG_FILS */ ++ ++#endif /* FILS_HLP_H */ +--- contrib/wpa/src/ap/gas_query_ap.c.orig ++++ contrib/wpa/src/ap/gas_query_ap.c +@@ -0,0 +1,714 @@ ++/* ++ * Generic advertisement service (GAS) query (hostapd) ++ * Copyright (c) 2009, Atheros Communications ++ * Copyright (c) 2011-2017, Qualcomm Atheros, Inc. ++ * Copyright (c) 2011-2014, 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 "utils/eloop.h" ++#include "utils/list.h" ++#include "common/ieee802_11_defs.h" ++#include "common/gas.h" ++#include "common/wpa_ctrl.h" ++#include "hostapd.h" ++#include "sta_info.h" ++#include "ap_drv_ops.h" ++#include "gas_query_ap.h" ++ ++ ++/** GAS query timeout in seconds */ ++#define GAS_QUERY_TIMEOUT_PERIOD 2 ++ ++/* GAS query wait-time / duration in ms */ ++#define GAS_QUERY_WAIT_TIME_INITIAL 1000 ++#define GAS_QUERY_WAIT_TIME_COMEBACK 150 ++ ++/** ++ * struct gas_query_pending - Pending GAS query ++ */ ++struct gas_query_pending { ++ struct dl_list list; ++ struct gas_query_ap *gas; ++ u8 addr[ETH_ALEN]; ++ u8 dialog_token; ++ u8 next_frag_id; ++ unsigned int wait_comeback:1; ++ unsigned int offchannel_tx_started:1; ++ unsigned int retry:1; ++ int freq; ++ u16 status_code; ++ struct wpabuf *req; ++ struct wpabuf *adv_proto; ++ struct wpabuf *resp; ++ struct os_reltime last_oper; ++ void (*cb)(void *ctx, const u8 *dst, u8 dialog_token, ++ enum gas_query_ap_result result, ++ const struct wpabuf *adv_proto, ++ const struct wpabuf *resp, u16 status_code); ++ void *ctx; ++ u8 sa[ETH_ALEN]; ++}; ++ ++/** ++ * struct gas_query_ap - Internal GAS query data ++ */ ++struct gas_query_ap { ++ struct hostapd_data *hapd; ++ void *msg_ctx; ++ struct dl_list pending; /* struct gas_query_pending */ ++ struct gas_query_pending *current; ++}; ++ ++ ++static void gas_query_tx_comeback_timeout(void *eloop_data, void *user_ctx); ++static void gas_query_timeout(void *eloop_data, void *user_ctx); ++static void gas_query_rx_comeback_timeout(void *eloop_data, void *user_ctx); ++static void gas_query_tx_initial_req(struct gas_query_ap *gas, ++ struct gas_query_pending *query); ++static int gas_query_new_dialog_token(struct gas_query_ap *gas, const u8 *dst); ++ ++ ++static int ms_from_time(struct os_reltime *last) ++{ ++ struct os_reltime now, res; ++ ++ os_get_reltime(&now); ++ os_reltime_sub(&now, last, &res); ++ return res.sec * 1000 + res.usec / 1000; ++} ++ ++ ++/** ++ * gas_query_ap_init - Initialize GAS query component ++ * @hapd: Pointer to hostapd data ++ * Returns: Pointer to GAS query data or %NULL on failure ++ */ ++struct gas_query_ap * gas_query_ap_init(struct hostapd_data *hapd, ++ void *msg_ctx) ++{ ++ struct gas_query_ap *gas; ++ ++ gas = os_zalloc(sizeof(*gas)); ++ if (!gas) ++ return NULL; ++ ++ gas->hapd = hapd; ++ gas->msg_ctx = msg_ctx; ++ dl_list_init(&gas->pending); ++ ++ return gas; ++} ++ ++ ++static const char * gas_result_txt(enum gas_query_ap_result result) ++{ ++ switch (result) { ++ case GAS_QUERY_AP_SUCCESS: ++ return "SUCCESS"; ++ case GAS_QUERY_AP_FAILURE: ++ return "FAILURE"; ++ case GAS_QUERY_AP_TIMEOUT: ++ return "TIMEOUT"; ++ case GAS_QUERY_AP_PEER_ERROR: ++ return "PEER_ERROR"; ++ case GAS_QUERY_AP_INTERNAL_ERROR: ++ return "INTERNAL_ERROR"; ++ case GAS_QUERY_AP_DELETED_AT_DEINIT: ++ return "DELETED_AT_DEINIT"; ++ } ++ ++ return "N/A"; ++} ++ ++ ++static void gas_query_free(struct gas_query_pending *query, int del_list) ++{ ++ if (del_list) ++ dl_list_del(&query->list); ++ ++ wpabuf_free(query->req); ++ wpabuf_free(query->adv_proto); ++ wpabuf_free(query->resp); ++ os_free(query); ++} ++ ++ ++static void gas_query_done(struct gas_query_ap *gas, ++ struct gas_query_pending *query, ++ enum gas_query_ap_result result) ++{ ++ wpa_msg(gas->msg_ctx, MSG_INFO, GAS_QUERY_DONE "addr=" MACSTR ++ " dialog_token=%u freq=%d status_code=%u result=%s", ++ MAC2STR(query->addr), query->dialog_token, query->freq, ++ query->status_code, gas_result_txt(result)); ++ if (gas->current == query) ++ gas->current = NULL; ++ eloop_cancel_timeout(gas_query_tx_comeback_timeout, gas, query); ++ eloop_cancel_timeout(gas_query_timeout, gas, query); ++ eloop_cancel_timeout(gas_query_rx_comeback_timeout, gas, query); ++ dl_list_del(&query->list); ++ query->cb(query->ctx, query->addr, query->dialog_token, result, ++ query->adv_proto, query->resp, query->status_code); ++ gas_query_free(query, 0); ++} ++ ++ ++/** ++ * gas_query_ap_deinit - Deinitialize GAS query component ++ * @gas: GAS query data from gas_query_init() ++ */ ++void gas_query_ap_deinit(struct gas_query_ap *gas) ++{ ++ struct gas_query_pending *query, *next; ++ ++ if (gas == NULL) ++ return; ++ ++ dl_list_for_each_safe(query, next, &gas->pending, ++ struct gas_query_pending, list) ++ gas_query_done(gas, query, GAS_QUERY_AP_DELETED_AT_DEINIT); ++ ++ os_free(gas); ++} ++ ++ ++static struct gas_query_pending * ++gas_query_get_pending(struct gas_query_ap *gas, const u8 *addr, u8 dialog_token) ++{ ++ struct gas_query_pending *q; ++ dl_list_for_each(q, &gas->pending, struct gas_query_pending, list) { ++ if (os_memcmp(q->addr, addr, ETH_ALEN) == 0 && ++ q->dialog_token == dialog_token) ++ return q; ++ } ++ return NULL; ++} ++ ++ ++static int gas_query_append(struct gas_query_pending *query, const u8 *data, ++ size_t len) ++{ ++ if (wpabuf_resize(&query->resp, len) < 0) { ++ wpa_printf(MSG_DEBUG, "GAS: No memory to store the response"); ++ return -1; ++ } ++ wpabuf_put_data(query->resp, data, len); ++ return 0; ++} ++ ++ ++void gas_query_ap_tx_status(struct gas_query_ap *gas, const u8 *dst, ++ const u8 *data, size_t data_len, int ok) ++{ ++ struct gas_query_pending *query; ++ int dur; ++ ++ if (!gas || !gas->current) { ++ wpa_printf(MSG_DEBUG, "GAS: Unexpected TX status: dst=" MACSTR ++ " ok=%d - no query in progress", MAC2STR(dst), ok); ++ return; ++ } ++ ++ query = gas->current; ++ ++ dur = ms_from_time(&query->last_oper); ++ wpa_printf(MSG_DEBUG, "GAS: TX status: dst=" MACSTR ++ " ok=%d query=%p dialog_token=%u dur=%d ms", ++ MAC2STR(dst), ok, query, query->dialog_token, dur); ++ if (os_memcmp(dst, query->addr, ETH_ALEN) != 0) { ++ wpa_printf(MSG_DEBUG, "GAS: TX status for unexpected destination"); ++ return; ++ } ++ os_get_reltime(&query->last_oper); ++ ++ eloop_cancel_timeout(gas_query_timeout, gas, query); ++ if (!ok) { ++ wpa_printf(MSG_DEBUG, "GAS: No ACK to GAS request"); ++ eloop_register_timeout(0, 250000, gas_query_timeout, ++ gas, query); ++ } else { ++ eloop_register_timeout(GAS_QUERY_TIMEOUT_PERIOD, 0, ++ gas_query_timeout, gas, query); ++ } ++ if (query->wait_comeback && !query->retry) { ++ eloop_cancel_timeout(gas_query_rx_comeback_timeout, ++ gas, query); ++ eloop_register_timeout( ++ 0, (GAS_QUERY_WAIT_TIME_COMEBACK + 10) * 1000, ++ gas_query_rx_comeback_timeout, gas, query); ++ } ++} ++ ++ ++static int pmf_in_use(struct hostapd_data *hapd, const u8 *addr) ++{ ++ struct sta_info *sta; ++ ++ sta = ap_get_sta(hapd, addr); ++ return sta && (sta->flags & WLAN_STA_MFP); ++} ++ ++ ++static int gas_query_tx(struct gas_query_ap *gas, ++ struct gas_query_pending *query, ++ struct wpabuf *req, unsigned int wait_time) ++{ ++ int res, prot = pmf_in_use(gas->hapd, query->addr); ++ ++ wpa_printf(MSG_DEBUG, "GAS: Send action frame to " MACSTR " len=%u " ++ "freq=%d prot=%d using src addr " MACSTR, ++ MAC2STR(query->addr), (unsigned int) wpabuf_len(req), ++ query->freq, prot, MAC2STR(query->sa)); ++ if (prot) { ++ u8 *categ = wpabuf_mhead_u8(req); ++ *categ = WLAN_ACTION_PROTECTED_DUAL; ++ } ++ os_get_reltime(&query->last_oper); ++ res = hostapd_drv_send_action(gas->hapd, query->freq, wait_time, ++ query->addr, wpabuf_head(req), ++ wpabuf_len(req)); ++ return res; ++} ++ ++ ++static void gas_query_tx_comeback_req(struct gas_query_ap *gas, ++ struct gas_query_pending *query) ++{ ++ struct wpabuf *req; ++ unsigned int wait_time; ++ ++ req = gas_build_comeback_req(query->dialog_token); ++ if (req == NULL) { ++ gas_query_done(gas, query, GAS_QUERY_AP_INTERNAL_ERROR); ++ return; ++ } ++ ++ wait_time = (query->retry || !query->offchannel_tx_started) ? ++ GAS_QUERY_WAIT_TIME_INITIAL : GAS_QUERY_WAIT_TIME_COMEBACK; ++ ++ if (gas_query_tx(gas, query, req, wait_time) < 0) { ++ wpa_printf(MSG_DEBUG, "GAS: Failed to send Action frame to " ++ MACSTR, MAC2STR(query->addr)); ++ gas_query_done(gas, query, GAS_QUERY_AP_INTERNAL_ERROR); ++ } ++ ++ wpabuf_free(req); ++} ++ ++ ++static void gas_query_rx_comeback_timeout(void *eloop_data, void *user_ctx) ++{ ++ struct gas_query_ap *gas = eloop_data; ++ struct gas_query_pending *query = user_ctx; ++ int dialog_token; ++ ++ wpa_printf(MSG_DEBUG, ++ "GAS: No response to comeback request received (retry=%u)", ++ query->retry); ++ if (gas->current != query || query->retry) ++ return; ++ dialog_token = gas_query_new_dialog_token(gas, query->addr); ++ if (dialog_token < 0) ++ return; ++ wpa_printf(MSG_DEBUG, ++ "GAS: Retry GAS query due to comeback response timeout"); ++ query->retry = 1; ++ query->dialog_token = dialog_token; ++ *(wpabuf_mhead_u8(query->req) + 2) = dialog_token; ++ query->wait_comeback = 0; ++ query->next_frag_id = 0; ++ wpabuf_free(query->adv_proto); ++ query->adv_proto = NULL; ++ eloop_cancel_timeout(gas_query_tx_comeback_timeout, gas, query); ++ eloop_cancel_timeout(gas_query_timeout, gas, query); ++ gas_query_tx_initial_req(gas, query); ++} ++ ++ ++static void gas_query_tx_comeback_timeout(void *eloop_data, void *user_ctx) ++{ ++ struct gas_query_ap *gas = eloop_data; ++ struct gas_query_pending *query = user_ctx; ++ ++ wpa_printf(MSG_DEBUG, "GAS: Comeback timeout for request to " MACSTR, ++ MAC2STR(query->addr)); ++ gas_query_tx_comeback_req(gas, query); ++} ++ ++ ++static void gas_query_tx_comeback_req_delay(struct gas_query_ap *gas, ++ struct gas_query_pending *query, ++ u16 comeback_delay) ++{ ++ unsigned int secs, usecs; ++ ++ secs = (comeback_delay * 1024) / 1000000; ++ usecs = comeback_delay * 1024 - secs * 1000000; ++ wpa_printf(MSG_DEBUG, "GAS: Send comeback request to " MACSTR ++ " in %u secs %u usecs", MAC2STR(query->addr), secs, usecs); ++ eloop_cancel_timeout(gas_query_tx_comeback_timeout, gas, query); ++ eloop_register_timeout(secs, usecs, gas_query_tx_comeback_timeout, ++ gas, query); ++} ++ ++ ++static void gas_query_rx_initial(struct gas_query_ap *gas, ++ struct gas_query_pending *query, ++ const u8 *adv_proto, const u8 *resp, ++ size_t len, u16 comeback_delay) ++{ ++ wpa_printf(MSG_DEBUG, "GAS: Received initial response from " ++ MACSTR " (dialog_token=%u comeback_delay=%u)", ++ MAC2STR(query->addr), query->dialog_token, comeback_delay); ++ ++ query->adv_proto = wpabuf_alloc_copy(adv_proto, 2 + adv_proto[1]); ++ if (query->adv_proto == NULL) { ++ gas_query_done(gas, query, GAS_QUERY_AP_INTERNAL_ERROR); ++ return; ++ } ++ ++ if (comeback_delay) { ++ eloop_cancel_timeout(gas_query_timeout, gas, query); ++ query->wait_comeback = 1; ++ gas_query_tx_comeback_req_delay(gas, query, comeback_delay); ++ return; ++ } ++ ++ /* Query was completed without comeback mechanism */ ++ if (gas_query_append(query, resp, len) < 0) { ++ gas_query_done(gas, query, GAS_QUERY_AP_INTERNAL_ERROR); ++ return; ++ } ++ ++ gas_query_done(gas, query, GAS_QUERY_AP_SUCCESS); ++} ++ ++ ++static void gas_query_rx_comeback(struct gas_query_ap *gas, ++ struct gas_query_pending *query, ++ const u8 *adv_proto, const u8 *resp, ++ size_t len, u8 frag_id, u8 more_frags, ++ u16 comeback_delay) ++{ ++ wpa_printf(MSG_DEBUG, "GAS: Received comeback response from " ++ MACSTR " (dialog_token=%u frag_id=%u more_frags=%u " ++ "comeback_delay=%u)", ++ MAC2STR(query->addr), query->dialog_token, frag_id, ++ more_frags, comeback_delay); ++ eloop_cancel_timeout(gas_query_rx_comeback_timeout, gas, query); ++ ++ if ((size_t) 2 + adv_proto[1] != wpabuf_len(query->adv_proto) || ++ os_memcmp(adv_proto, wpabuf_head(query->adv_proto), ++ wpabuf_len(query->adv_proto)) != 0) { ++ wpa_printf(MSG_DEBUG, "GAS: Advertisement Protocol changed " ++ "between initial and comeback response from " ++ MACSTR, MAC2STR(query->addr)); ++ gas_query_done(gas, query, GAS_QUERY_AP_PEER_ERROR); ++ return; ++ } ++ ++ if (comeback_delay) { ++ if (frag_id) { ++ wpa_printf(MSG_DEBUG, "GAS: Invalid comeback response " ++ "with non-zero frag_id and comeback_delay " ++ "from " MACSTR, MAC2STR(query->addr)); ++ gas_query_done(gas, query, GAS_QUERY_AP_PEER_ERROR); ++ return; ++ } ++ gas_query_tx_comeback_req_delay(gas, query, comeback_delay); ++ return; ++ } ++ ++ if (frag_id != query->next_frag_id) { ++ wpa_printf(MSG_DEBUG, "GAS: Unexpected frag_id in response " ++ "from " MACSTR, MAC2STR(query->addr)); ++ if (frag_id + 1 == query->next_frag_id) { ++ wpa_printf(MSG_DEBUG, "GAS: Drop frame as possible " ++ "retry of previous fragment"); ++ return; ++ } ++ gas_query_done(gas, query, GAS_QUERY_AP_PEER_ERROR); ++ return; ++ } ++ query->next_frag_id++; ++ ++ if (gas_query_append(query, resp, len) < 0) { ++ gas_query_done(gas, query, GAS_QUERY_AP_INTERNAL_ERROR); ++ return; ++ } ++ ++ if (more_frags) { ++ gas_query_tx_comeback_req(gas, query); ++ return; ++ } ++ ++ gas_query_done(gas, query, GAS_QUERY_AP_SUCCESS); ++} ++ ++ ++/** ++ * gas_query_ap_rx - Indicate reception of a Public Action or Protected Dual ++ * frame ++ * @gas: GAS query data from gas_query_init() ++ * @sa: Source MAC address of the Action frame ++ * @categ: Category of the Action frame ++ * @data: Payload of the Action frame ++ * @len: Length of @data ++ * @freq: Frequency (in MHz) on which the frame was received ++ * Returns: 0 if the Public Action frame was a GAS frame or -1 if not ++ */ ++int gas_query_ap_rx(struct gas_query_ap *gas, const u8 *sa, u8 categ, ++ const u8 *data, size_t len, int freq) ++{ ++ struct gas_query_pending *query; ++ u8 action, dialog_token, frag_id = 0, more_frags = 0; ++ u16 comeback_delay, resp_len; ++ const u8 *pos, *adv_proto; ++ int prot, pmf; ++ unsigned int left; ++ ++ if (!gas || len < 4) ++ return -1; ++ ++ pos = data; ++ action = *pos++; ++ dialog_token = *pos++; ++ ++ if (action != WLAN_PA_GAS_INITIAL_RESP && ++ action != WLAN_PA_GAS_COMEBACK_RESP) ++ return -1; /* Not a GAS response */ ++ ++ prot = categ == WLAN_ACTION_PROTECTED_DUAL; ++ pmf = pmf_in_use(gas->hapd, sa); ++ if (prot && !pmf) { ++ wpa_printf(MSG_DEBUG, "GAS: Drop unexpected protected GAS frame when PMF is disabled"); ++ return 0; ++ } ++ if (!prot && pmf) { ++ wpa_printf(MSG_DEBUG, "GAS: Drop unexpected unprotected GAS frame when PMF is enabled"); ++ return 0; ++ } ++ ++ query = gas_query_get_pending(gas, sa, dialog_token); ++ if (query == NULL) { ++ wpa_printf(MSG_DEBUG, "GAS: No pending query found for " MACSTR ++ " dialog token %u", MAC2STR(sa), dialog_token); ++ return -1; ++ } ++ ++ wpa_printf(MSG_DEBUG, "GAS: Response in %d ms from " MACSTR, ++ ms_from_time(&query->last_oper), MAC2STR(sa)); ++ ++ if (query->wait_comeback && action == WLAN_PA_GAS_INITIAL_RESP) { ++ wpa_printf(MSG_DEBUG, "GAS: Unexpected initial response from " ++ MACSTR " dialog token %u when waiting for comeback " ++ "response", MAC2STR(sa), dialog_token); ++ return 0; ++ } ++ ++ if (!query->wait_comeback && action == WLAN_PA_GAS_COMEBACK_RESP) { ++ wpa_printf(MSG_DEBUG, "GAS: Unexpected comeback response from " ++ MACSTR " dialog token %u when waiting for initial " ++ "response", MAC2STR(sa), dialog_token); ++ return 0; ++ } ++ ++ query->status_code = WPA_GET_LE16(pos); ++ pos += 2; ++ ++ if (query->status_code == WLAN_STATUS_QUERY_RESP_OUTSTANDING && ++ action == WLAN_PA_GAS_COMEBACK_RESP) { ++ wpa_printf(MSG_DEBUG, "GAS: Allow non-zero status for outstanding comeback response"); ++ } else if (query->status_code != WLAN_STATUS_SUCCESS) { ++ wpa_printf(MSG_DEBUG, "GAS: Query to " MACSTR " dialog token " ++ "%u failed - status code %u", ++ MAC2STR(sa), dialog_token, query->status_code); ++ gas_query_done(gas, query, GAS_QUERY_AP_FAILURE); ++ return 0; ++ } ++ ++ if (action == WLAN_PA_GAS_COMEBACK_RESP) { ++ if (pos + 1 > data + len) ++ return 0; ++ frag_id = *pos & 0x7f; ++ more_frags = (*pos & 0x80) >> 7; ++ pos++; ++ } ++ ++ /* Comeback Delay */ ++ if (pos + 2 > data + len) ++ return 0; ++ comeback_delay = WPA_GET_LE16(pos); ++ pos += 2; ++ ++ /* Advertisement Protocol element */ ++ if (pos + 2 > data + len || pos + 2 + pos[1] > data + len) { ++ wpa_printf(MSG_DEBUG, "GAS: No room for Advertisement " ++ "Protocol element in the response from " MACSTR, ++ MAC2STR(sa)); ++ return 0; ++ } ++ ++ if (*pos != WLAN_EID_ADV_PROTO) { ++ wpa_printf(MSG_DEBUG, "GAS: Unexpected Advertisement " ++ "Protocol element ID %u in response from " MACSTR, ++ *pos, MAC2STR(sa)); ++ return 0; ++ } ++ ++ adv_proto = pos; ++ pos += 2 + pos[1]; ++ ++ /* Query Response Length */ ++ if (pos + 2 > data + len) { ++ wpa_printf(MSG_DEBUG, "GAS: No room for GAS Response Length"); ++ return 0; ++ } ++ resp_len = WPA_GET_LE16(pos); ++ pos += 2; ++ ++ left = data + len - pos; ++ if (resp_len > left) { ++ wpa_printf(MSG_DEBUG, "GAS: Truncated Query Response in " ++ "response from " MACSTR, MAC2STR(sa)); ++ return 0; ++ } ++ ++ if (resp_len < left) { ++ wpa_printf(MSG_DEBUG, "GAS: Ignore %u octets of extra data " ++ "after Query Response from " MACSTR, ++ left - resp_len, MAC2STR(sa)); ++ } ++ ++ if (action == WLAN_PA_GAS_COMEBACK_RESP) ++ gas_query_rx_comeback(gas, query, adv_proto, pos, resp_len, ++ frag_id, more_frags, comeback_delay); ++ else ++ gas_query_rx_initial(gas, query, adv_proto, pos, resp_len, ++ comeback_delay); ++ ++ return 0; ++} ++ ++ ++static void gas_query_timeout(void *eloop_data, void *user_ctx) ++{ ++ struct gas_query_ap *gas = eloop_data; ++ struct gas_query_pending *query = user_ctx; ++ ++ wpa_printf(MSG_DEBUG, "GAS: No response received for query to " MACSTR ++ " dialog token %u", ++ MAC2STR(query->addr), query->dialog_token); ++ gas_query_done(gas, query, GAS_QUERY_AP_TIMEOUT); ++} ++ ++ ++static int gas_query_dialog_token_available(struct gas_query_ap *gas, ++ const u8 *dst, u8 dialog_token) ++{ ++ struct gas_query_pending *q; ++ dl_list_for_each(q, &gas->pending, struct gas_query_pending, list) { ++ if (os_memcmp(dst, q->addr, ETH_ALEN) == 0 && ++ dialog_token == q->dialog_token) ++ return 0; ++ } ++ ++ return 1; ++} ++ ++ ++static void gas_query_tx_initial_req(struct gas_query_ap *gas, ++ struct gas_query_pending *query) ++{ ++ if (gas_query_tx(gas, query, query->req, ++ GAS_QUERY_WAIT_TIME_INITIAL) < 0) { ++ wpa_printf(MSG_DEBUG, "GAS: Failed to send Action frame to " ++ MACSTR, MAC2STR(query->addr)); ++ gas_query_done(gas, query, GAS_QUERY_AP_INTERNAL_ERROR); ++ return; ++ } ++ gas->current = query; ++ ++ wpa_printf(MSG_DEBUG, "GAS: Starting query timeout for dialog token %u", ++ query->dialog_token); ++ eloop_register_timeout(GAS_QUERY_TIMEOUT_PERIOD, 0, ++ gas_query_timeout, gas, query); ++} ++ ++ ++static int gas_query_new_dialog_token(struct gas_query_ap *gas, const u8 *dst) ++{ ++ static int next_start = 0; ++ int dialog_token; ++ ++ for (dialog_token = 0; dialog_token < 256; dialog_token++) { ++ if (gas_query_dialog_token_available( ++ gas, dst, (next_start + dialog_token) % 256)) ++ break; ++ } ++ if (dialog_token == 256) ++ return -1; /* Too many pending queries */ ++ dialog_token = (next_start + dialog_token) % 256; ++ next_start = (dialog_token + 1) % 256; ++ return dialog_token; ++} ++ ++ ++/** ++ * gas_query_ap_req - Request a GAS query ++ * @gas: GAS query data from gas_query_init() ++ * @dst: Destination MAC address for the query ++ * @freq: Frequency (in MHz) for the channel on which to send the query ++ * @req: GAS query payload (to be freed by gas_query module in case of success ++ * return) ++ * @cb: Callback function for reporting GAS query result and response ++ * @ctx: Context pointer to use with the @cb call ++ * Returns: dialog token (>= 0) on success or -1 on failure ++ */ ++int gas_query_ap_req(struct gas_query_ap *gas, const u8 *dst, int freq, ++ struct wpabuf *req, ++ void (*cb)(void *ctx, const u8 *dst, u8 dialog_token, ++ enum gas_query_ap_result result, ++ const struct wpabuf *adv_proto, ++ const struct wpabuf *resp, u16 status_code), ++ void *ctx) ++{ ++ struct gas_query_pending *query; ++ int dialog_token; ++ ++ if (!gas || wpabuf_len(req) < 3) ++ return -1; ++ ++ dialog_token = gas_query_new_dialog_token(gas, dst); ++ if (dialog_token < 0) ++ return -1; ++ ++ query = os_zalloc(sizeof(*query)); ++ if (query == NULL) ++ return -1; ++ ++ query->gas = gas; ++ os_memcpy(query->addr, dst, ETH_ALEN); ++ query->dialog_token = dialog_token; ++ query->freq = freq; ++ query->cb = cb; ++ query->ctx = ctx; ++ query->req = req; ++ dl_list_add(&gas->pending, &query->list); ++ ++ *(wpabuf_mhead_u8(req) + 2) = dialog_token; ++ ++ wpa_msg(gas->msg_ctx, MSG_INFO, GAS_QUERY_START "addr=" MACSTR ++ " dialog_token=%u freq=%d", ++ MAC2STR(query->addr), query->dialog_token, query->freq); ++ ++ gas_query_tx_initial_req(gas, query); ++ ++ return dialog_token; ++} +--- contrib/wpa/src/ap/gas_query_ap.h.orig ++++ contrib/wpa/src/ap/gas_query_ap.h +@@ -0,0 +1,43 @@ ++/* ++ * Generic advertisement service (GAS) query ++ * Copyright (c) 2009, Atheros Communications ++ * Copyright (c) 2011-2017, Qualcomm Atheros ++ * ++ * This software may be distributed under the terms of the BSD license. ++ * See README for more details. ++ */ ++ ++#ifndef GAS_QUERY_AP_H ++#define GAS_QUERY_AP_H ++ ++struct gas_query_ap; ++ ++struct gas_query_ap * gas_query_ap_init(struct hostapd_data *hapd, ++ void *msg_ctx); ++void gas_query_ap_deinit(struct gas_query_ap *gas); ++int gas_query_ap_rx(struct gas_query_ap *gas, const u8 *sa, u8 categ, ++ const u8 *data, size_t len, int freq); ++ ++/** ++ * enum gas_query_ap_result - GAS query result ++ */ ++enum gas_query_ap_result { ++ GAS_QUERY_AP_SUCCESS, ++ GAS_QUERY_AP_FAILURE, ++ GAS_QUERY_AP_TIMEOUT, ++ GAS_QUERY_AP_PEER_ERROR, ++ GAS_QUERY_AP_INTERNAL_ERROR, ++ GAS_QUERY_AP_DELETED_AT_DEINIT ++}; ++ ++int gas_query_ap_req(struct gas_query_ap *gas, const u8 *dst, int freq, ++ struct wpabuf *req, ++ void (*cb)(void *ctx, const u8 *dst, u8 dialog_token, ++ enum gas_query_ap_result result, ++ const struct wpabuf *adv_proto, ++ const struct wpabuf *resp, u16 status_code), ++ void *ctx); ++void gas_query_ap_tx_status(struct gas_query_ap *gas, const u8 *dst, ++ const u8 *data, size_t data_len, int ok); ++ ++#endif /* GAS_QUERY_AP_H */ +--- contrib/wpa/src/ap/gas_serv.c.orig ++++ contrib/wpa/src/ap/gas_serv.c +@@ -11,14 +11,31 @@ + #include "common.h" + #include "common/ieee802_11_defs.h" + #include "common/gas.h" ++#include "common/wpa_ctrl.h" + #include "utils/eloop.h" + #include "hostapd.h" + #include "ap_config.h" + #include "ap_drv_ops.h" ++#include "dpp_hostapd.h" + #include "sta_info.h" + #include "gas_serv.h" + + ++#ifdef CONFIG_DPP ++static void gas_serv_write_dpp_adv_proto(struct wpabuf *buf) ++{ ++ wpabuf_put_u8(buf, WLAN_EID_ADV_PROTO); ++ wpabuf_put_u8(buf, 8); /* Length */ ++ wpabuf_put_u8(buf, 0x7f); ++ wpabuf_put_u8(buf, WLAN_EID_VENDOR_SPECIFIC); ++ wpabuf_put_u8(buf, 5); ++ wpabuf_put_be24(buf, OUI_WFA); ++ wpabuf_put_u8(buf, DPP_OUI_TYPE); ++ wpabuf_put_u8(buf, 0x01); ++} ++#endif /* CONFIG_DPP */ ++ ++ + static void convert_to_protected_dual(struct wpabuf *msg) + { + u8 *categ = wpabuf_mhead_u8(msg); +@@ -50,9 +67,12 @@ + sta->flags |= WLAN_STA_GAS; + /* + * The default inactivity is 300 seconds. We don't need +- * it to be that long. ++ * it to be that long. Use five second timeout and increase this ++ * with the comeback_delay for testing cases. + */ +- ap_sta_session_timeout(hapd, sta, 5); ++ ap_sta_session_timeout(hapd, sta, ++ hapd->conf->gas_comeback_delay / 1024 + ++ 5); + } else { + ap_sta_replenish_timeout(hapd, sta, 5); + } +@@ -101,6 +121,7 @@ + if (sta->gas_dialog[i].dialog_token != dialog_token || + !sta->gas_dialog[i].valid) + continue; ++ ap_sta_replenish_timeout(hapd, sta, 5); + return &sta->gas_dialog[i]; + } + wpa_printf(MSG_DEBUG, "ANQP: Could not find dialog for " +@@ -160,34 +181,127 @@ + wpabuf_put_u8(buf, HS20_STYPE_OPERATING_CLASS); + if (hapd->conf->hs20_osu_providers_count) + wpabuf_put_u8(buf, HS20_STYPE_OSU_PROVIDERS_LIST); ++ if (hapd->conf->hs20_osu_providers_nai_count) ++ wpabuf_put_u8(buf, HS20_STYPE_OSU_PROVIDERS_NAI_LIST); + if (hapd->conf->hs20_icons_count) + wpabuf_put_u8(buf, HS20_STYPE_ICON_REQUEST); ++ if (hapd->conf->hs20_operator_icon_count) ++ wpabuf_put_u8(buf, HS20_STYPE_OPERATOR_ICON_METADATA); + gas_anqp_set_element_len(buf, len); + } + #endif /* CONFIG_HS20 */ + + ++static struct anqp_element * get_anqp_elem(struct hostapd_data *hapd, ++ u16 infoid) ++{ ++ struct anqp_element *elem; ++ ++ dl_list_for_each(elem, &hapd->conf->anqp_elem, struct anqp_element, ++ list) { ++ if (elem->infoid == infoid) ++ return elem; ++ } ++ ++ return NULL; ++} ++ ++ ++static void anqp_add_elem(struct hostapd_data *hapd, struct wpabuf *buf, ++ u16 infoid) ++{ ++ struct anqp_element *elem; ++ ++ elem = get_anqp_elem(hapd, infoid); ++ if (!elem) ++ return; ++ if (wpabuf_tailroom(buf) < 2 + 2 + wpabuf_len(elem->payload)) { ++ wpa_printf(MSG_DEBUG, "ANQP: No room for InfoID %u payload", ++ infoid); ++ return; ++ } ++ ++ wpabuf_put_le16(buf, infoid); ++ wpabuf_put_le16(buf, wpabuf_len(elem->payload)); ++ wpabuf_put_buf(buf, elem->payload); ++} ++ ++ ++static int anqp_add_override(struct hostapd_data *hapd, struct wpabuf *buf, ++ u16 infoid) ++{ ++ if (get_anqp_elem(hapd, infoid)) { ++ anqp_add_elem(hapd, buf, infoid); ++ return 1; ++ } ++ ++ return 0; ++} ++ ++ + static void anqp_add_capab_list(struct hostapd_data *hapd, + struct wpabuf *buf) + { + u8 *len; ++ u16 id; + ++ if (anqp_add_override(hapd, buf, ANQP_CAPABILITY_LIST)) ++ return; ++ + len = gas_anqp_add_element(buf, ANQP_CAPABILITY_LIST); + wpabuf_put_le16(buf, ANQP_CAPABILITY_LIST); +- if (hapd->conf->venue_name) ++ if (hapd->conf->venue_name || get_anqp_elem(hapd, ANQP_VENUE_NAME)) + wpabuf_put_le16(buf, ANQP_VENUE_NAME); +- if (hapd->conf->network_auth_type) ++ if (get_anqp_elem(hapd, ANQP_EMERGENCY_CALL_NUMBER)) ++ wpabuf_put_le16(buf, ANQP_EMERGENCY_CALL_NUMBER); ++ if (hapd->conf->network_auth_type || ++ get_anqp_elem(hapd, ANQP_NETWORK_AUTH_TYPE)) + wpabuf_put_le16(buf, ANQP_NETWORK_AUTH_TYPE); +- if (hapd->conf->roaming_consortium) ++ if (hapd->conf->roaming_consortium || ++ get_anqp_elem(hapd, ANQP_ROAMING_CONSORTIUM)) + wpabuf_put_le16(buf, ANQP_ROAMING_CONSORTIUM); +- if (hapd->conf->ipaddr_type_configured) ++ if (hapd->conf->ipaddr_type_configured || ++ get_anqp_elem(hapd, ANQP_IP_ADDR_TYPE_AVAILABILITY)) + wpabuf_put_le16(buf, ANQP_IP_ADDR_TYPE_AVAILABILITY); +- if (hapd->conf->nai_realm_data) ++ if (hapd->conf->nai_realm_data || ++ get_anqp_elem(hapd, ANQP_NAI_REALM)) + wpabuf_put_le16(buf, ANQP_NAI_REALM); +- if (hapd->conf->anqp_3gpp_cell_net) ++ if (hapd->conf->anqp_3gpp_cell_net || ++ get_anqp_elem(hapd, ANQP_3GPP_CELLULAR_NETWORK)) + wpabuf_put_le16(buf, ANQP_3GPP_CELLULAR_NETWORK); +- if (hapd->conf->domain_name) ++ if (get_anqp_elem(hapd, ANQP_AP_GEOSPATIAL_LOCATION)) ++ wpabuf_put_le16(buf, ANQP_AP_GEOSPATIAL_LOCATION); ++ if (get_anqp_elem(hapd, ANQP_AP_CIVIC_LOCATION)) ++ wpabuf_put_le16(buf, ANQP_AP_CIVIC_LOCATION); ++ if (get_anqp_elem(hapd, ANQP_AP_LOCATION_PUBLIC_URI)) ++ wpabuf_put_le16(buf, ANQP_AP_LOCATION_PUBLIC_URI); ++ if (hapd->conf->domain_name || get_anqp_elem(hapd, ANQP_DOMAIN_NAME)) + wpabuf_put_le16(buf, ANQP_DOMAIN_NAME); ++ if (get_anqp_elem(hapd, ANQP_EMERGENCY_ALERT_URI)) ++ wpabuf_put_le16(buf, ANQP_EMERGENCY_ALERT_URI); ++ if (get_anqp_elem(hapd, ANQP_TDLS_CAPABILITY)) ++ wpabuf_put_le16(buf, ANQP_TDLS_CAPABILITY); ++ if (get_anqp_elem(hapd, ANQP_EMERGENCY_NAI)) ++ wpabuf_put_le16(buf, ANQP_EMERGENCY_NAI); ++ if (get_anqp_elem(hapd, ANQP_NEIGHBOR_REPORT)) ++ wpabuf_put_le16(buf, ANQP_NEIGHBOR_REPORT); ++#ifdef CONFIG_FILS ++ if (!dl_list_empty(&hapd->conf->fils_realms) || ++ get_anqp_elem(hapd, ANQP_FILS_REALM_INFO)) ++ wpabuf_put_le16(buf, ANQP_FILS_REALM_INFO); ++#endif /* CONFIG_FILS */ ++ if (get_anqp_elem(hapd, ANQP_CAG)) ++ wpabuf_put_le16(buf, ANQP_CAG); ++ if (hapd->conf->venue_url || get_anqp_elem(hapd, ANQP_VENUE_URL)) ++ wpabuf_put_le16(buf, ANQP_VENUE_URL); ++ if (get_anqp_elem(hapd, ANQP_ADVICE_OF_CHARGE)) ++ wpabuf_put_le16(buf, ANQP_ADVICE_OF_CHARGE); ++ if (get_anqp_elem(hapd, ANQP_LOCAL_CONTENT)) ++ wpabuf_put_le16(buf, ANQP_LOCAL_CONTENT); ++ for (id = 280; id < 300; id++) { ++ if (get_anqp_elem(hapd, id)) ++ wpabuf_put_le16(buf, id); ++ } + #ifdef CONFIG_HS20 + anqp_add_hs_capab_list(hapd, buf); + #endif /* CONFIG_HS20 */ +@@ -197,6 +311,9 @@ + + static void anqp_add_venue_name(struct hostapd_data *hapd, struct wpabuf *buf) + { ++ if (anqp_add_override(hapd, buf, ANQP_VENUE_NAME)) ++ return; ++ + if (hapd->conf->venue_name) { + u8 *len; + unsigned int i; +@@ -215,9 +332,35 @@ + } + + ++static void anqp_add_venue_url(struct hostapd_data *hapd, struct wpabuf *buf) ++{ ++ if (anqp_add_override(hapd, buf, ANQP_VENUE_URL)) ++ return; ++ ++ if (hapd->conf->venue_url) { ++ u8 *len; ++ unsigned int i; ++ ++ len = gas_anqp_add_element(buf, ANQP_VENUE_URL); ++ for (i = 0; i < hapd->conf->venue_url_count; i++) { ++ struct hostapd_venue_url *url; ++ ++ url = &hapd->conf->venue_url[i]; ++ wpabuf_put_u8(buf, 1 + url->url_len); ++ wpabuf_put_u8(buf, url->venue_number); ++ wpabuf_put_data(buf, url->url, url->url_len); ++ } ++ gas_anqp_set_element_len(buf, len); ++ } ++} ++ ++ + static void anqp_add_network_auth_type(struct hostapd_data *hapd, + struct wpabuf *buf) + { ++ if (anqp_add_override(hapd, buf, ANQP_NETWORK_AUTH_TYPE)) ++ return; ++ + if (hapd->conf->network_auth_type) { + wpabuf_put_le16(buf, ANQP_NETWORK_AUTH_TYPE); + wpabuf_put_le16(buf, hapd->conf->network_auth_type_len); +@@ -233,6 +376,9 @@ + unsigned int i; + u8 *len; + ++ if (anqp_add_override(hapd, buf, ANQP_ROAMING_CONSORTIUM)) ++ return; ++ + len = gas_anqp_add_element(buf, ANQP_ROAMING_CONSORTIUM); + for (i = 0; i < hapd->conf->roaming_consortium_count; i++) { + struct hostapd_roaming_consortium *rc; +@@ -247,6 +393,9 @@ + static void anqp_add_ip_addr_type_availability(struct hostapd_data *hapd, + struct wpabuf *buf) + { ++ if (anqp_add_override(hapd, buf, ANQP_IP_ADDR_TYPE_AVAILABILITY)) ++ return; ++ + if (hapd->conf->ipaddr_type_configured) { + wpabuf_put_le16(buf, ANQP_IP_ADDR_TYPE_AVAILABILITY); + wpabuf_put_le16(buf, 1); +@@ -309,7 +458,7 @@ + + pos = home_realm; + end = pos + home_realm_len; +- if (pos + 1 > end) { ++ if (end - pos < 1) { + wpa_hexdump(MSG_DEBUG, "Too short NAI Home Realm Query", + home_realm, home_realm_len); + return -1; +@@ -317,7 +466,7 @@ + num_realms = *pos++; + + for (i = 0; i < num_realms && num_matching < 10; i++) { +- if (pos + 2 > end) { ++ if (end - pos < 2) { + wpa_hexdump(MSG_DEBUG, + "Truncated NAI Home Realm Query", + home_realm, home_realm_len); +@@ -325,7 +474,7 @@ + } + encoding = *pos++; + realm_len = *pos++; +- if (pos + realm_len > end) { ++ if (realm_len > end - pos) { + wpa_hexdump(MSG_DEBUG, + "Truncated NAI Home Realm Query", + home_realm, home_realm_len); +@@ -391,6 +540,10 @@ + const u8 *home_realm, size_t home_realm_len, + int nai_realm, int nai_home_realm) + { ++ if (nai_realm && !nai_home_realm && ++ anqp_add_override(hapd, buf, ANQP_NAI_REALM)) ++ return; ++ + if (nai_realm && hapd->conf->nai_realm_data) { + u8 *len; + unsigned int i, j; +@@ -424,6 +577,9 @@ + static void anqp_add_3gpp_cellular_network(struct hostapd_data *hapd, + struct wpabuf *buf) + { ++ if (anqp_add_override(hapd, buf, ANQP_3GPP_CELLULAR_NETWORK)) ++ return; ++ + if (hapd->conf->anqp_3gpp_cell_net) { + wpabuf_put_le16(buf, ANQP_3GPP_CELLULAR_NETWORK); + wpabuf_put_le16(buf, +@@ -436,6 +592,9 @@ + + static void anqp_add_domain_name(struct hostapd_data *hapd, struct wpabuf *buf) + { ++ if (anqp_add_override(hapd, buf, ANQP_DOMAIN_NAME)) ++ return; ++ + if (hapd->conf->domain_name) { + wpabuf_put_le16(buf, ANQP_DOMAIN_NAME); + wpabuf_put_le16(buf, hapd->conf->domain_name_len); +@@ -445,6 +604,36 @@ + } + + ++#ifdef CONFIG_FILS ++static void anqp_add_fils_realm_info(struct hostapd_data *hapd, ++ struct wpabuf *buf) ++{ ++ size_t count; ++ ++ if (anqp_add_override(hapd, buf, ANQP_FILS_REALM_INFO)) ++ return; ++ ++ count = dl_list_len(&hapd->conf->fils_realms); ++ if (count > 10000) ++ count = 10000; ++ if (count) { ++ struct fils_realm *realm; ++ ++ wpabuf_put_le16(buf, ANQP_FILS_REALM_INFO); ++ wpabuf_put_le16(buf, 2 * count); ++ ++ dl_list_for_each(realm, &hapd->conf->fils_realms, ++ struct fils_realm, list) { ++ if (count == 0) ++ break; ++ wpabuf_put_data(buf, realm->hash, 2); ++ count--; ++ } ++ } ++} ++#endif /* CONFIG_FILS */ ++ ++ + #ifdef CONFIG_HS20 + + static void anqp_add_operator_friendly_name(struct hostapd_data *hapd, +@@ -518,6 +707,29 @@ + } + + ++static void anqp_add_icon(struct wpabuf *buf, struct hostapd_bss_config *bss, ++ const char *name) ++{ ++ size_t j; ++ struct hs20_icon *icon = NULL; ++ ++ for (j = 0; j < bss->hs20_icons_count && !icon; j++) { ++ if (os_strcmp(name, bss->hs20_icons[j].name) == 0) ++ icon = &bss->hs20_icons[j]; ++ } ++ if (!icon) ++ return; /* icon info not found */ ++ ++ wpabuf_put_le16(buf, icon->width); ++ wpabuf_put_le16(buf, icon->height); ++ wpabuf_put_data(buf, icon->language, 3); ++ wpabuf_put_u8(buf, os_strlen(icon->type)); ++ wpabuf_put_str(buf, icon->type); ++ wpabuf_put_u8(buf, os_strlen(icon->name)); ++ wpabuf_put_str(buf, icon->name); ++} ++ ++ + static void anqp_add_osu_provider(struct wpabuf *buf, + struct hostapd_bss_config *bss, + struct hs20_osu_provider *p) +@@ -546,32 +758,14 @@ + + /* OSU Method List */ + count = wpabuf_put(buf, 1); +- for (i = 0; p->method_list[i] >= 0; i++) ++ for (i = 0; p->method_list && p->method_list[i] >= 0; i++) + wpabuf_put_u8(buf, p->method_list[i]); + *count = i; + + /* Icons Available */ + len2 = wpabuf_put(buf, 2); +- for (i = 0; i < p->icons_count; i++) { +- size_t j; +- struct hs20_icon *icon = NULL; +- +- for (j = 0; j < bss->hs20_icons_count && !icon; j++) { +- if (os_strcmp(p->icons[i], bss->hs20_icons[j].name) == +- 0) +- icon = &bss->hs20_icons[j]; +- } +- if (!icon) +- continue; /* icon info not found */ +- +- wpabuf_put_le16(buf, icon->width); +- wpabuf_put_le16(buf, icon->height); +- wpabuf_put_data(buf, icon->language, 3); +- wpabuf_put_u8(buf, os_strlen(icon->type)); +- wpabuf_put_str(buf, icon->type); +- wpabuf_put_u8(buf, os_strlen(icon->name)); +- wpabuf_put_str(buf, icon->name); +- } ++ for (i = 0; i < p->icons_count; i++) ++ anqp_add_icon(buf, bss, p->icons[i]); + WPA_PUT_LE16(len2, (u8 *) wpabuf_put(buf, 0) - len2 - 2); + + /* OSU_NAI */ +@@ -625,6 +819,40 @@ + } + + ++static void anqp_add_osu_provider_nai(struct wpabuf *buf, ++ struct hs20_osu_provider *p) ++{ ++ /* OSU_NAI for shared BSS (Single SSID) */ ++ if (p->osu_nai2) { ++ wpabuf_put_u8(buf, os_strlen(p->osu_nai2)); ++ wpabuf_put_str(buf, p->osu_nai2); ++ } else { ++ wpabuf_put_u8(buf, 0); ++ } ++} ++ ++ ++static void anqp_add_osu_providers_nai_list(struct hostapd_data *hapd, ++ struct wpabuf *buf) ++{ ++ if (hapd->conf->hs20_osu_providers_nai_count) { ++ size_t i; ++ u8 *len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC); ++ wpabuf_put_be24(buf, OUI_WFA); ++ wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE); ++ wpabuf_put_u8(buf, HS20_STYPE_OSU_PROVIDERS_NAI_LIST); ++ wpabuf_put_u8(buf, 0); /* Reserved */ ++ ++ for (i = 0; i < hapd->conf->hs20_osu_providers_count; i++) { ++ anqp_add_osu_provider_nai( ++ buf, &hapd->conf->hs20_osu_providers[i]); ++ } ++ ++ gas_anqp_set_element_len(buf, len); ++ } ++} ++ ++ + static void anqp_add_icon_binary_file(struct hostapd_data *hapd, + struct wpabuf *buf, + const u8 *name, size_t name_len) +@@ -680,17 +908,78 @@ + gas_anqp_set_element_len(buf, len); + } + ++ ++static void anqp_add_operator_icon_metadata(struct hostapd_data *hapd, ++ struct wpabuf *buf) ++{ ++ struct hostapd_bss_config *bss = hapd->conf; ++ size_t i; ++ u8 *len; ++ ++ if (!bss->hs20_operator_icon_count) ++ return; ++ ++ len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC); ++ ++ wpabuf_put_be24(buf, OUI_WFA); ++ wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE); ++ wpabuf_put_u8(buf, HS20_STYPE_OPERATOR_ICON_METADATA); ++ wpabuf_put_u8(buf, 0); /* Reserved */ ++ ++ for (i = 0; i < bss->hs20_operator_icon_count; i++) ++ anqp_add_icon(buf, bss, bss->hs20_operator_icon[i]); ++ ++ gas_anqp_set_element_len(buf, len); ++} ++ + #endif /* CONFIG_HS20 */ + + ++#ifdef CONFIG_MBO ++static void anqp_add_mbo_cell_data_conn_pref(struct hostapd_data *hapd, ++ struct wpabuf *buf) ++{ ++ if (hapd->conf->mbo_cell_data_conn_pref >= 0) { ++ u8 *len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC); ++ wpabuf_put_be24(buf, OUI_WFA); ++ wpabuf_put_u8(buf, MBO_ANQP_OUI_TYPE); ++ wpabuf_put_u8(buf, MBO_ANQP_SUBTYPE_CELL_CONN_PREF); ++ wpabuf_put_u8(buf, hapd->conf->mbo_cell_data_conn_pref); ++ gas_anqp_set_element_len(buf, len); ++ } ++} ++#endif /* CONFIG_MBO */ ++ ++ ++static size_t anqp_get_required_len(struct hostapd_data *hapd, ++ const u16 *infoid, ++ unsigned int num_infoid) ++{ ++ size_t len = 0; ++ unsigned int i; ++ ++ for (i = 0; i < num_infoid; i++) { ++ struct anqp_element *elem = get_anqp_elem(hapd, infoid[i]); ++ ++ if (elem) ++ len += 2 + 2 + wpabuf_len(elem->payload); ++ } ++ ++ return len; ++} ++ ++ + static struct wpabuf * + gas_serv_build_gas_resp_payload(struct hostapd_data *hapd, + unsigned int request, + const u8 *home_realm, size_t home_realm_len, +- const u8 *icon_name, size_t icon_name_len) ++ const u8 *icon_name, size_t icon_name_len, ++ const u16 *extra_req, ++ unsigned int num_extra_req) + { + struct wpabuf *buf; + size_t len; ++ unsigned int i; + + len = 1400; + if (request & (ANQP_REQ_NAI_REALM | ANQP_REQ_NAI_HOME_REALM)) +@@ -697,6 +986,11 @@ + len += 1000; + if (request & ANQP_REQ_ICON_REQUEST) + len += 65536; ++#ifdef CONFIG_FILS ++ if (request & ANQP_FILS_REALM_INFO) ++ len += 2 * dl_list_len(&hapd->conf->fils_realms); ++#endif /* CONFIG_FILS */ ++ len += anqp_get_required_len(hapd, extra_req, num_extra_req); + + buf = wpabuf_alloc(len); + if (buf == NULL) +@@ -706,6 +1000,8 @@ + anqp_add_capab_list(hapd, buf); + if (request & ANQP_REQ_VENUE_NAME) + anqp_add_venue_name(hapd, buf); ++ if (request & ANQP_REQ_EMERGENCY_CALL_NUMBER) ++ anqp_add_elem(hapd, buf, ANQP_EMERGENCY_CALL_NUMBER); + if (request & ANQP_REQ_NETWORK_AUTH_TYPE) + anqp_add_network_auth_type(hapd, buf); + if (request & ANQP_REQ_ROAMING_CONSORTIUM) +@@ -718,9 +1014,35 @@ + request & ANQP_REQ_NAI_HOME_REALM); + if (request & ANQP_REQ_3GPP_CELLULAR_NETWORK) + anqp_add_3gpp_cellular_network(hapd, buf); ++ if (request & ANQP_REQ_AP_GEOSPATIAL_LOCATION) ++ anqp_add_elem(hapd, buf, ANQP_AP_GEOSPATIAL_LOCATION); ++ if (request & ANQP_REQ_AP_CIVIC_LOCATION) ++ anqp_add_elem(hapd, buf, ANQP_AP_CIVIC_LOCATION); ++ if (request & ANQP_REQ_AP_LOCATION_PUBLIC_URI) ++ anqp_add_elem(hapd, buf, ANQP_AP_LOCATION_PUBLIC_URI); + if (request & ANQP_REQ_DOMAIN_NAME) + anqp_add_domain_name(hapd, buf); ++ if (request & ANQP_REQ_EMERGENCY_ALERT_URI) ++ anqp_add_elem(hapd, buf, ANQP_EMERGENCY_ALERT_URI); ++ if (request & ANQP_REQ_TDLS_CAPABILITY) ++ anqp_add_elem(hapd, buf, ANQP_TDLS_CAPABILITY); ++ if (request & ANQP_REQ_EMERGENCY_NAI) ++ anqp_add_elem(hapd, buf, ANQP_EMERGENCY_NAI); + ++ for (i = 0; i < num_extra_req; i++) { ++#ifdef CONFIG_FILS ++ if (extra_req[i] == ANQP_FILS_REALM_INFO) { ++ anqp_add_fils_realm_info(hapd, buf); ++ continue; ++ } ++#endif /* CONFIG_FILS */ ++ if (extra_req[i] == ANQP_VENUE_URL) { ++ anqp_add_venue_url(hapd, buf); ++ continue; ++ } ++ anqp_add_elem(hapd, buf, extra_req[i]); ++ } ++ + #ifdef CONFIG_HS20 + if (request & ANQP_REQ_HS_CAPABILITY_LIST) + anqp_add_hs_capab_list(hapd, buf); +@@ -736,12 +1058,23 @@ + anqp_add_osu_providers_list(hapd, buf); + if (request & ANQP_REQ_ICON_REQUEST) + anqp_add_icon_binary_file(hapd, buf, icon_name, icon_name_len); ++ if (request & ANQP_REQ_OPERATOR_ICON_METADATA) ++ anqp_add_operator_icon_metadata(hapd, buf); ++ if (request & ANQP_REQ_OSU_PROVIDERS_NAI_LIST) ++ anqp_add_osu_providers_nai_list(hapd, buf); + #endif /* CONFIG_HS20 */ + ++#ifdef CONFIG_MBO ++ if (request & ANQP_REQ_MBO_CELL_DATA_CONN_PREF) ++ anqp_add_mbo_cell_data_conn_pref(hapd, buf); ++#endif /* CONFIG_MBO */ ++ + return buf; + } + + ++#define ANQP_MAX_EXTRA_REQ 20 ++ + struct anqp_query_info { + unsigned int request; + const u8 *home_realm_query; +@@ -749,6 +1082,8 @@ + const u8 *icon_name; + size_t icon_name_len; + int p2p_sd; ++ u16 extra_req[ANQP_MAX_EXTRA_REQ]; ++ unsigned int num_extra_req; + }; + + +@@ -776,6 +1111,11 @@ + set_anqp_req(ANQP_REQ_VENUE_NAME, "Venue Name", + hapd->conf->venue_name != NULL, qi); + break; ++ case ANQP_EMERGENCY_CALL_NUMBER: ++ set_anqp_req(ANQP_REQ_EMERGENCY_CALL_NUMBER, ++ "Emergency Call Number", ++ get_anqp_elem(hapd, info_id) != NULL, qi); ++ break; + case ANQP_NETWORK_AUTH_TYPE: + set_anqp_req(ANQP_REQ_NETWORK_AUTH_TYPE, "Network Auth Type", + hapd->conf->network_auth_type != NULL, qi); +@@ -798,13 +1138,65 @@ + "3GPP Cellular Network", + hapd->conf->anqp_3gpp_cell_net != NULL, qi); + break; ++ case ANQP_AP_GEOSPATIAL_LOCATION: ++ set_anqp_req(ANQP_REQ_AP_GEOSPATIAL_LOCATION, ++ "AP Geospatial Location", ++ get_anqp_elem(hapd, info_id) != NULL, qi); ++ break; ++ case ANQP_AP_CIVIC_LOCATION: ++ set_anqp_req(ANQP_REQ_AP_CIVIC_LOCATION, ++ "AP Civic Location", ++ get_anqp_elem(hapd, info_id) != NULL, qi); ++ break; ++ case ANQP_AP_LOCATION_PUBLIC_URI: ++ set_anqp_req(ANQP_REQ_AP_LOCATION_PUBLIC_URI, ++ "AP Location Public URI", ++ get_anqp_elem(hapd, info_id) != NULL, qi); ++ break; + case ANQP_DOMAIN_NAME: + set_anqp_req(ANQP_REQ_DOMAIN_NAME, "Domain Name", + hapd->conf->domain_name != NULL, qi); + break; ++ case ANQP_EMERGENCY_ALERT_URI: ++ set_anqp_req(ANQP_REQ_EMERGENCY_ALERT_URI, ++ "Emergency Alert URI", ++ get_anqp_elem(hapd, info_id) != NULL, qi); ++ break; ++ case ANQP_TDLS_CAPABILITY: ++ set_anqp_req(ANQP_REQ_TDLS_CAPABILITY, ++ "TDLS Capability", ++ get_anqp_elem(hapd, info_id) != NULL, qi); ++ break; ++ case ANQP_EMERGENCY_NAI: ++ set_anqp_req(ANQP_REQ_EMERGENCY_NAI, ++ "Emergency NAI", ++ get_anqp_elem(hapd, info_id) != NULL, qi); ++ break; + default: +- wpa_printf(MSG_DEBUG, "ANQP: Unsupported Info Id %u", +- info_id); ++#ifdef CONFIG_FILS ++ if (info_id == ANQP_FILS_REALM_INFO && ++ !dl_list_empty(&hapd->conf->fils_realms)) { ++ wpa_printf(MSG_DEBUG, ++ "ANQP: FILS Realm Information (local)"); ++ } else ++#endif /* CONFIG_FILS */ ++ if (info_id == ANQP_VENUE_URL && hapd->conf->venue_url) { ++ wpa_printf(MSG_DEBUG, ++ "ANQP: Venue URL (local)"); ++ } else if (!get_anqp_elem(hapd, info_id)) { ++ wpa_printf(MSG_DEBUG, "ANQP: Unsupported Info Id %u", ++ info_id); ++ break; ++ } ++ if (qi->num_extra_req == ANQP_MAX_EXTRA_REQ) { ++ wpa_printf(MSG_DEBUG, ++ "ANQP: No more room for extra requests - ignore Info Id %u", ++ info_id); ++ break; ++ } ++ wpa_printf(MSG_DEBUG, "ANQP: Info Id %u (local)", info_id); ++ qi->extra_req[qi->num_extra_req] = info_id; ++ qi->num_extra_req++; + break; + } + } +@@ -817,7 +1209,7 @@ + wpa_printf(MSG_DEBUG, "ANQP: %u Info IDs requested in Query list", + (unsigned int) (end - pos) / 2); + +- while (pos + 2 <= end) { ++ while (end - pos >= 2) { + rx_anqp_query_list_id(hapd, WPA_GET_LE16(pos), qi); + pos += 2; + } +@@ -857,6 +1249,16 @@ + set_anqp_req(ANQP_REQ_OSU_PROVIDERS_LIST, "OSU Providers list", + hapd->conf->hs20_osu_providers_count, qi); + break; ++ case HS20_STYPE_OPERATOR_ICON_METADATA: ++ set_anqp_req(ANQP_REQ_OPERATOR_ICON_METADATA, ++ "Operator Icon Metadata", ++ hapd->conf->hs20_operator_icon_count, qi); ++ break; ++ case HS20_STYPE_OSU_PROVIDERS_NAI_LIST: ++ set_anqp_req(ANQP_REQ_OSU_PROVIDERS_NAI_LIST, ++ "OSU Providers NAI List", ++ hapd->conf->hs20_osu_providers_nai_count, qi); ++ break; + default: + wpa_printf(MSG_DEBUG, "ANQP: Unsupported HS 2.0 subtype %u", + subtype); +@@ -899,52 +1301,15 @@ + } + + +-static void rx_anqp_vendor_specific(struct hostapd_data *hapd, +- const u8 *pos, const u8 *end, +- struct anqp_query_info *qi) ++static void rx_anqp_vendor_specific_hs20(struct hostapd_data *hapd, ++ const u8 *pos, const u8 *end, ++ struct anqp_query_info *qi) + { +- u32 oui; + u8 subtype; + +- if (pos + 4 > end) { +- wpa_printf(MSG_DEBUG, "ANQP: Too short vendor specific ANQP " +- "Query element"); ++ if (end - pos <= 1) + return; +- } + +- oui = WPA_GET_BE24(pos); +- pos += 3; +- if (oui != OUI_WFA) { +- wpa_printf(MSG_DEBUG, "ANQP: Unsupported vendor OUI %06x", +- oui); +- return; +- } +- +-#ifdef CONFIG_P2P +- if (*pos == P2P_OUI_TYPE) { +- /* +- * This is for P2P SD and will be taken care of by the P2P +- * implementation. This query needs to be ignored in the generic +- * GAS server to avoid duplicated response. +- */ +- wpa_printf(MSG_DEBUG, +- "ANQP: Ignore WFA vendor type %u (P2P SD) in generic GAS server", +- *pos); +- qi->p2p_sd = 1; +- return; +- } +-#endif /* CONFIG_P2P */ +- +- if (*pos != HS20_ANQP_OUI_TYPE) { +- wpa_printf(MSG_DEBUG, "ANQP: Unsupported WFA vendor type %u", +- *pos); +- return; +- } +- pos++; +- +- if (pos + 1 >= end) +- return; +- + subtype = *pos++; + pos++; /* Reserved */ + switch (subtype) { +@@ -971,9 +1336,119 @@ + #endif /* CONFIG_HS20 */ + + ++#ifdef CONFIG_P2P ++static void rx_anqp_vendor_specific_p2p(struct hostapd_data *hapd, ++ struct anqp_query_info *qi) ++{ ++ /* ++ * This is for P2P SD and will be taken care of by the P2P ++ * implementation. This query needs to be ignored in the generic ++ * GAS server to avoid duplicated response. ++ */ ++ wpa_printf(MSG_DEBUG, ++ "ANQP: Ignore WFA vendor type %u (P2P SD) in generic GAS server", ++ P2P_OUI_TYPE); ++ qi->p2p_sd = 1; ++ return; ++} ++#endif /* CONFIG_P2P */ ++ ++ ++#ifdef CONFIG_MBO ++ ++static void rx_anqp_mbo_query_list(struct hostapd_data *hapd, u8 subtype, ++ struct anqp_query_info *qi) ++{ ++ switch (subtype) { ++ case MBO_ANQP_SUBTYPE_CELL_CONN_PREF: ++ set_anqp_req(ANQP_REQ_MBO_CELL_DATA_CONN_PREF, ++ "Cellular Data Connection Preference", ++ hapd->conf->mbo_cell_data_conn_pref >= 0, qi); ++ break; ++ default: ++ wpa_printf(MSG_DEBUG, "ANQP: Unsupported MBO subtype %u", ++ subtype); ++ break; ++ } ++} ++ ++ ++static void rx_anqp_vendor_specific_mbo(struct hostapd_data *hapd, ++ const u8 *pos, const u8 *end, ++ struct anqp_query_info *qi) ++{ ++ u8 subtype; ++ ++ if (end - pos < 1) ++ return; ++ ++ subtype = *pos++; ++ switch (subtype) { ++ case MBO_ANQP_SUBTYPE_QUERY_LIST: ++ wpa_printf(MSG_DEBUG, "ANQP: MBO Query List"); ++ while (pos < end) { ++ rx_anqp_mbo_query_list(hapd, *pos, qi); ++ pos++; ++ } ++ break; ++ default: ++ wpa_printf(MSG_DEBUG, "ANQP: Unsupported MBO query subtype %u", ++ subtype); ++ break; ++ } ++} ++ ++#endif /* CONFIG_MBO */ ++ ++ ++static void rx_anqp_vendor_specific(struct hostapd_data *hapd, ++ const u8 *pos, const u8 *end, ++ struct anqp_query_info *qi) ++{ ++ u32 oui; ++ ++ if (end - pos < 4) { ++ wpa_printf(MSG_DEBUG, "ANQP: Too short vendor specific ANQP " ++ "Query element"); ++ return; ++ } ++ ++ oui = WPA_GET_BE24(pos); ++ pos += 3; ++ if (oui != OUI_WFA) { ++ wpa_printf(MSG_DEBUG, "ANQP: Unsupported vendor OUI %06x", ++ oui); ++ return; ++ } ++ ++ switch (*pos) { ++#ifdef CONFIG_P2P ++ case P2P_OUI_TYPE: ++ rx_anqp_vendor_specific_p2p(hapd, qi); ++ break; ++#endif /* CONFIG_P2P */ ++#ifdef CONFIG_HS20 ++ case HS20_ANQP_OUI_TYPE: ++ rx_anqp_vendor_specific_hs20(hapd, pos + 1, end, qi); ++ break; ++#endif /* CONFIG_HS20 */ ++#ifdef CONFIG_MBO ++ case MBO_ANQP_OUI_TYPE: ++ rx_anqp_vendor_specific_mbo(hapd, pos + 1, end, qi); ++ break; ++#endif /* CONFIG_MBO */ ++ default: ++ wpa_printf(MSG_DEBUG, "ANQP: Unsupported WFA vendor type %u", ++ *pos); ++ break; ++ } ++} ++ ++ + static void gas_serv_req_local_processing(struct hostapd_data *hapd, + const u8 *sa, u8 dialog_token, +- struct anqp_query_info *qi, int prot) ++ struct anqp_query_info *qi, int prot, ++ int std_addr3) + { + struct wpabuf *buf, *tx_buf; + +@@ -980,7 +1455,8 @@ + buf = gas_serv_build_gas_resp_payload(hapd, qi->request, + qi->home_realm_query, + qi->home_realm_query_len, +- qi->icon_name, qi->icon_name_len); ++ qi->icon_name, qi->icon_name_len, ++ qi->extra_req, qi->num_extra_req); + wpa_hexdump_buf(MSG_MSGDUMP, "ANQP: Locally generated ANQP responses", + buf); + if (!buf) +@@ -994,7 +1470,7 @@ + } + #endif /* CONFIG_P2P */ + +- if (wpabuf_len(buf) > hapd->gas_frag_limit || ++ if (wpabuf_len(buf) > hapd->conf->gas_frag_limit || + hapd->conf->gas_comeback_delay) { + struct gas_dialog_info *di; + u16 comeback_delay = 1; +@@ -1033,15 +1509,88 @@ + return; + if (prot) + convert_to_protected_dual(tx_buf); ++ if (std_addr3) ++ hostapd_drv_send_action(hapd, hapd->iface->freq, 0, sa, ++ wpabuf_head(tx_buf), ++ wpabuf_len(tx_buf)); ++ else ++ hostapd_drv_send_action_addr3_ap(hapd, hapd->iface->freq, 0, sa, ++ wpabuf_head(tx_buf), ++ wpabuf_len(tx_buf)); ++ wpabuf_free(tx_buf); ++} ++ ++ ++#ifdef CONFIG_DPP ++static void gas_serv_req_dpp_processing(struct hostapd_data *hapd, ++ const u8 *sa, u8 dialog_token, ++ int prot, struct wpabuf *buf) ++{ ++ struct wpabuf *tx_buf; ++ ++ if (wpabuf_len(buf) > hapd->conf->gas_frag_limit || ++ hapd->conf->gas_comeback_delay) { ++ struct gas_dialog_info *di; ++ u16 comeback_delay = 1; ++ ++ if (hapd->conf->gas_comeback_delay) { ++ /* Testing - allow overriding of the delay value */ ++ comeback_delay = hapd->conf->gas_comeback_delay; ++ } ++ ++ wpa_printf(MSG_DEBUG, ++ "DPP: Too long response to fit in initial response - use GAS comeback"); ++ di = gas_dialog_create(hapd, sa, dialog_token); ++ if (!di) { ++ wpa_printf(MSG_INFO, "DPP: Could not create dialog for " ++ MACSTR " (dialog token %u)", ++ MAC2STR(sa), dialog_token); ++ wpabuf_free(buf); ++ tx_buf = gas_build_initial_resp( ++ dialog_token, WLAN_STATUS_UNSPECIFIED_FAILURE, ++ 0, 10); ++ if (tx_buf) ++ gas_serv_write_dpp_adv_proto(tx_buf); ++ } else { ++ di->prot = prot; ++ di->sd_resp = buf; ++ di->sd_resp_pos = 0; ++ tx_buf = gas_build_initial_resp( ++ dialog_token, WLAN_STATUS_SUCCESS, ++ comeback_delay, 10); ++ if (tx_buf) ++ gas_serv_write_dpp_adv_proto(tx_buf); ++ } ++ } else { ++ wpa_printf(MSG_DEBUG, ++ "DPP: GAS Initial response (no comeback)"); ++ tx_buf = gas_build_initial_resp( ++ dialog_token, WLAN_STATUS_SUCCESS, 0, ++ 10 + 2 + wpabuf_len(buf)); ++ if (tx_buf) { ++ gas_serv_write_dpp_adv_proto(tx_buf); ++ wpabuf_put_le16(tx_buf, wpabuf_len(buf)); ++ wpabuf_put_buf(tx_buf, buf); ++ hostapd_dpp_gas_status_handler(hapd, 1); ++ } ++ wpabuf_free(buf); ++ } ++ if (!tx_buf) ++ return; ++ if (prot) ++ convert_to_protected_dual(tx_buf); + hostapd_drv_send_action(hapd, hapd->iface->freq, 0, sa, +- wpabuf_head(tx_buf), wpabuf_len(tx_buf)); ++ wpabuf_head(tx_buf), ++ wpabuf_len(tx_buf)); + wpabuf_free(tx_buf); + } ++#endif /* CONFIG_DPP */ + + + static void gas_serv_rx_gas_initial_req(struct hostapd_data *hapd, + const u8 *sa, +- const u8 *data, size_t len, int prot) ++ const u8 *data, size_t len, int prot, ++ int std_addr3) + { + const u8 *pos = data; + const u8 *end = data + len; +@@ -1050,6 +1599,9 @@ + u16 slen; + struct anqp_query_info qi; + const u8 *adv_proto; ++#ifdef CONFIG_DPP ++ int dpp = 0; ++#endif /* CONFIG_DPP */ + + if (len < 1 + 2) + return; +@@ -1069,14 +1621,23 @@ + adv_proto = pos++; + + slen = *pos++; +- next = pos + slen; +- if (next > end || slen < 2) { ++ if (slen > end - pos || slen < 2) { + wpa_msg(hapd->msg_ctx, MSG_DEBUG, + "GAS: Invalid IE in GAS Initial Request"); + return; + } ++ next = pos + slen; + pos++; /* skip QueryRespLenLimit and PAME-BI */ + ++#ifdef CONFIG_DPP ++ if (slen == 8 && *pos == WLAN_EID_VENDOR_SPECIFIC && ++ pos[1] == 5 && WPA_GET_BE24(&pos[2]) == OUI_WFA && ++ pos[5] == DPP_OUI_TYPE && pos[6] == 0x01) { ++ wpa_printf(MSG_DEBUG, "DPP: Configuration Request"); ++ dpp = 1; ++ } else ++#endif /* CONFIG_DPP */ ++ + if (*pos != ACCESS_NETWORK_QUERY_PROTOCOL) { + struct wpabuf *buf; + wpa_msg(hapd->msg_ctx, MSG_DEBUG, +@@ -1093,8 +1654,15 @@ + wpabuf_put_le16(buf, 0); /* Query Response Length */ + if (prot) + convert_to_protected_dual(buf); +- hostapd_drv_send_action(hapd, hapd->iface->freq, 0, sa, +- wpabuf_head(buf), wpabuf_len(buf)); ++ if (std_addr3) ++ hostapd_drv_send_action(hapd, hapd->iface->freq, 0, sa, ++ wpabuf_head(buf), ++ wpabuf_len(buf)); ++ else ++ hostapd_drv_send_action_addr3_ap(hapd, ++ hapd->iface->freq, 0, ++ sa, wpabuf_head(buf), ++ wpabuf_len(buf)); + wpabuf_free(buf); + return; + } +@@ -1101,19 +1669,31 @@ + + pos = next; + /* Query Request */ +- if (pos + 2 > end) ++ if (end - pos < 2) + return; + slen = WPA_GET_LE16(pos); + pos += 2; +- if (pos + slen > end) ++ if (slen > end - pos) + return; + end = pos + slen; + ++#ifdef CONFIG_DPP ++ if (dpp) { ++ struct wpabuf *msg; ++ ++ msg = hostapd_dpp_gas_req_handler(hapd, sa, pos, slen); ++ if (!msg) ++ return; ++ gas_serv_req_dpp_processing(hapd, sa, dialog_token, prot, msg); ++ return; ++ } ++#endif /* CONFIG_DPP */ ++ + /* ANQP Query Request */ + while (pos < end) { + u16 info_id, elen; + +- if (pos + 4 > end) ++ if (end - pos < 4) + return; + + info_id = WPA_GET_LE16(pos); +@@ -1121,7 +1701,7 @@ + elen = WPA_GET_LE16(pos); + pos += 2; + +- if (pos + elen > end) { ++ if (elen > end - pos) { + wpa_printf(MSG_DEBUG, "ANQP: Invalid Query Request"); + return; + } +@@ -1130,11 +1710,9 @@ + case ANQP_QUERY_LIST: + rx_anqp_query_list(hapd, pos, pos + elen, &qi); + break; +-#ifdef CONFIG_HS20 + case ANQP_VENDOR_SPECIFIC: + rx_anqp_vendor_specific(hapd, pos, pos + elen, &qi); + break; +-#endif /* CONFIG_HS20 */ + default: + wpa_printf(MSG_DEBUG, "ANQP: Unsupported Query " + "Request element %u", info_id); +@@ -1144,13 +1722,15 @@ + pos += elen; + } + +- gas_serv_req_local_processing(hapd, sa, dialog_token, &qi, prot); ++ gas_serv_req_local_processing(hapd, sa, dialog_token, &qi, prot, ++ std_addr3); + } + + + static void gas_serv_rx_gas_comeback_req(struct hostapd_data *hapd, + const u8 *sa, +- const u8 *data, size_t len, int prot) ++ const u8 *data, size_t len, int prot, ++ int std_addr3) + { + struct gas_dialog_info *dialog; + struct wpabuf *buf, *tx_buf; +@@ -1182,8 +1762,8 @@ + } + + frag_len = wpabuf_len(dialog->sd_resp) - dialog->sd_resp_pos; +- if (frag_len > hapd->gas_frag_limit) { +- frag_len = hapd->gas_frag_limit; ++ if (frag_len > hapd->conf->gas_frag_limit) { ++ frag_len = hapd->conf->gas_frag_limit; + more = 1; + } + wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: resp frag_len %u", +@@ -1196,6 +1776,18 @@ + gas_serv_dialog_clear(dialog); + return; + } ++#ifdef CONFIG_DPP ++ if (dialog->dpp) { ++ tx_buf = gas_build_comeback_resp(dialog_token, ++ WLAN_STATUS_SUCCESS, ++ dialog->sd_frag_id, more, 0, ++ 10 + frag_len); ++ if (tx_buf) { ++ gas_serv_write_dpp_adv_proto(tx_buf); ++ wpabuf_put_buf(tx_buf, buf); ++ } ++ } else ++#endif /* CONFIG_DPP */ + tx_buf = gas_anqp_build_comeback_resp_buf(dialog_token, + WLAN_STATUS_SUCCESS, + dialog->sd_frag_id, +@@ -1219,6 +1811,10 @@ + } else { + wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: All fragments of " + "SD response sent"); ++#ifdef CONFIG_DPP ++ if (dialog->dpp) ++ hostapd_dpp_gas_status_handler(hapd, 1); ++#endif /* CONFIG_DPP */ + gas_serv_dialog_clear(dialog); + gas_serv_free_dialogs(hapd, sa); + } +@@ -1226,8 +1822,14 @@ + send_resp: + if (prot) + convert_to_protected_dual(tx_buf); +- hostapd_drv_send_action(hapd, hapd->iface->freq, 0, sa, +- wpabuf_head(tx_buf), wpabuf_len(tx_buf)); ++ if (std_addr3) ++ hostapd_drv_send_action(hapd, hapd->iface->freq, 0, sa, ++ wpabuf_head(tx_buf), ++ wpabuf_len(tx_buf)); ++ else ++ hostapd_drv_send_action_addr3_ap(hapd, hapd->iface->freq, 0, sa, ++ wpabuf_head(tx_buf), ++ wpabuf_len(tx_buf)); + wpabuf_free(tx_buf); + } + +@@ -1238,7 +1840,7 @@ + struct hostapd_data *hapd = ctx; + const struct ieee80211_mgmt *mgmt; + const u8 *sa, *data; +- int prot; ++ int prot, std_addr3; + + mgmt = (const struct ieee80211_mgmt *) buf; + if (len < IEEE80211_HDRLEN + 2) +@@ -1253,14 +1855,22 @@ + */ + prot = mgmt->u.action.category == WLAN_ACTION_PROTECTED_DUAL; + sa = mgmt->sa; ++ if (hapd->conf->gas_address3 == 1) ++ std_addr3 = 1; ++ else if (hapd->conf->gas_address3 == 2) ++ std_addr3 = 0; ++ else ++ std_addr3 = is_broadcast_ether_addr(mgmt->bssid); + len -= IEEE80211_HDRLEN + 1; + data = buf + IEEE80211_HDRLEN + 1; + switch (data[0]) { + case WLAN_PA_GAS_INITIAL_REQ: +- gas_serv_rx_gas_initial_req(hapd, sa, data + 1, len - 1, prot); ++ gas_serv_rx_gas_initial_req(hapd, sa, data + 1, len - 1, prot, ++ std_addr3); + break; + case WLAN_PA_GAS_COMEBACK_REQ: +- gas_serv_rx_gas_comeback_req(hapd, sa, data + 1, len - 1, prot); ++ gas_serv_rx_gas_comeback_req(hapd, sa, data + 1, len - 1, prot, ++ std_addr3); + break; + } + } +@@ -1270,9 +1880,6 @@ + { + hapd->public_action_cb2 = gas_serv_rx_public_action; + hapd->public_action_cb2_ctx = hapd; +- hapd->gas_frag_limit = 1400; +- if (hapd->conf->gas_frag_limit > 0) +- hapd->gas_frag_limit = hapd->conf->gas_frag_limit; + return 0; + } + +--- contrib/wpa/src/ap/gas_serv.h.orig ++++ contrib/wpa/src/ap/gas_serv.h +@@ -9,10 +9,13 @@ + #ifndef GAS_SERV_H + #define GAS_SERV_H + ++/* First 16 ANQP InfoIDs can be included in the optimized bitmap */ + #define ANQP_REQ_CAPABILITY_LIST \ + (1 << (ANQP_CAPABILITY_LIST - ANQP_QUERY_LIST)) + #define ANQP_REQ_VENUE_NAME \ + (1 << (ANQP_VENUE_NAME - ANQP_QUERY_LIST)) ++#define ANQP_REQ_EMERGENCY_CALL_NUMBER \ ++ (1 << (ANQP_EMERGENCY_CALL_NUMBER - ANQP_QUERY_LIST)) + #define ANQP_REQ_NETWORK_AUTH_TYPE \ + (1 << (ANQP_NETWORK_AUTH_TYPE - ANQP_QUERY_LIST)) + #define ANQP_REQ_ROAMING_CONSORTIUM \ +@@ -23,8 +26,24 @@ + (1 << (ANQP_NAI_REALM - ANQP_QUERY_LIST)) + #define ANQP_REQ_3GPP_CELLULAR_NETWORK \ + (1 << (ANQP_3GPP_CELLULAR_NETWORK - ANQP_QUERY_LIST)) ++#define ANQP_REQ_AP_GEOSPATIAL_LOCATION \ ++ (1 << (ANQP_AP_GEOSPATIAL_LOCATION - ANQP_QUERY_LIST)) ++#define ANQP_REQ_AP_CIVIC_LOCATION \ ++ (1 << (ANQP_AP_CIVIC_LOCATION - ANQP_QUERY_LIST)) ++#define ANQP_REQ_AP_LOCATION_PUBLIC_URI \ ++ (1 << (ANQP_AP_LOCATION_PUBLIC_URI - ANQP_QUERY_LIST)) + #define ANQP_REQ_DOMAIN_NAME \ + (1 << (ANQP_DOMAIN_NAME - ANQP_QUERY_LIST)) ++#define ANQP_REQ_EMERGENCY_ALERT_URI \ ++ (1 << (ANQP_EMERGENCY_ALERT_URI - ANQP_QUERY_LIST)) ++#define ANQP_REQ_TDLS_CAPABILITY \ ++ (1 << (ANQP_TDLS_CAPABILITY - ANQP_QUERY_LIST)) ++#define ANQP_REQ_EMERGENCY_NAI \ ++ (1 << (ANQP_EMERGENCY_NAI - ANQP_QUERY_LIST)) ++/* ++ * First 15 Hotspot 2.0 vendor specific ANQP-elements can be included in the ++ * optimized bitmap. ++ */ + #define ANQP_REQ_HS_CAPABILITY_LIST \ + (0x10000 << HS20_STYPE_CAPABILITY_LIST) + #define ANQP_REQ_OPERATOR_FRIENDLY_NAME \ +@@ -41,6 +60,13 @@ + (0x10000 << HS20_STYPE_OSU_PROVIDERS_LIST) + #define ANQP_REQ_ICON_REQUEST \ + (0x10000 << HS20_STYPE_ICON_REQUEST) ++#define ANQP_REQ_OPERATOR_ICON_METADATA \ ++ (0x10000 << HS20_STYPE_OPERATOR_ICON_METADATA) ++#define ANQP_REQ_OSU_PROVIDERS_NAI_LIST \ ++ (0x10000 << HS20_STYPE_OSU_PROVIDERS_NAI_LIST) ++/* The first MBO ANQP-element can be included in the optimized bitmap. */ ++#define ANQP_REQ_MBO_CELL_DATA_CONN_PREF \ ++ (BIT(29) << MBO_ANQP_SUBTYPE_CELL_CONN_PREF) + + struct gas_dialog_info { + u8 valid; +@@ -49,6 +75,7 @@ + size_t sd_resp_pos; /* Offset in sd_resp */ + u8 sd_frag_id; + int prot; /* whether Protected Dual of Public Action frame is used */ ++ int dpp; /* whether this is a DPP Config Response */ + }; + + struct hostapd_data; +--- contrib/wpa/src/ap/hostapd.c.orig ++++ contrib/wpa/src/ap/hostapd.c +@@ -1,6 +1,6 @@ + /* + * hostapd / Initialization and configuration +- * Copyright (c) 2002-2014, Jouni Malinen ++ * Copyright (c) 2002-2019, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. +@@ -12,6 +12,7 @@ + #include "utils/eloop.h" + #include "common/ieee802_11_defs.h" + #include "common/wpa_ctrl.h" ++#include "common/hw_features_common.h" + #include "radius/radius_client.h" + #include "radius/radius_das.h" + #include "eap_server/tncs.h" +@@ -30,6 +31,8 @@ + #include "vlan_init.h" + #include "wpa_auth.h" + #include "wps_hostapd.h" ++#include "dpp_hostapd.h" ++#include "gas_query_ap.h" + #include "hw_features.h" + #include "wpa_auth_glue.h" + #include "ap_drv_ops.h" +@@ -42,6 +45,11 @@ + #include "x_snoop.h" + #include "dhcp_snoop.h" + #include "ndisc_snoop.h" ++#include "neighbor_db.h" ++#include "rrm.h" ++#include "fils_hlp.h" ++#include "acs.h" ++#include "hs20.h" + + + static int hostapd_flush_old_stations(struct hostapd_data *hapd, u16 reason); +@@ -49,6 +57,8 @@ + static int hostapd_broadcast_wep_clear(struct hostapd_data *hapd); + static int setup_interface2(struct hostapd_iface *iface); + static void channel_list_update_timeout(void *eloop_ctx, void *timeout_ctx); ++static void hostapd_interface_setup_failure_handler(void *eloop_ctx, ++ void *timeout_ctx); + + + int hostapd_for_each_interface(struct hapd_interfaces *interfaces, +@@ -68,10 +78,26 @@ + } + + ++void hostapd_reconfig_encryption(struct hostapd_data *hapd) ++{ ++ if (hapd->wpa_auth) ++ return; ++ ++ hostapd_set_privacy(hapd, 0); ++ hostapd_setup_encryption(hapd->conf->iface, hapd); ++} ++ ++ + static void hostapd_reload_bss(struct hostapd_data *hapd) + { + struct hostapd_ssid *ssid; + ++ if (!hapd->started) ++ return; ++ ++ if (hapd->conf->wmm_enabled < 0) ++ hapd->conf->wmm_enabled = hapd->iconf->ieee80211n; ++ + #ifndef CONFIG_NO_RADIUS + radius_client_reconfig(hapd->radius, hapd->conf->radius); + #endif /* CONFIG_NO_RADIUS */ +@@ -150,8 +176,27 @@ + } + + ++static int hostapd_iface_conf_changed(struct hostapd_config *newconf, ++ struct hostapd_config *oldconf) ++{ ++ size_t i; ++ ++ if (newconf->num_bss != oldconf->num_bss) ++ return 1; ++ ++ for (i = 0; i < newconf->num_bss; i++) { ++ if (os_strcmp(newconf->bss[i]->iface, ++ oldconf->bss[i]->iface) != 0) ++ return 1; ++ } ++ ++ return 0; ++} ++ ++ + int hostapd_reload_config(struct hostapd_iface *iface) + { ++ struct hapd_interfaces *interfaces = iface->interfaces; + struct hostapd_data *hapd = iface->bss[0]; + struct hostapd_config *newconf, *oldconf; + size_t j; +@@ -174,6 +219,35 @@ + hostapd_clear_old(iface); + + oldconf = hapd->iconf; ++ if (hostapd_iface_conf_changed(newconf, oldconf)) { ++ char *fname; ++ int res; ++ ++ wpa_printf(MSG_DEBUG, ++ "Configuration changes include interface/BSS modification - force full disable+enable sequence"); ++ fname = os_strdup(iface->config_fname); ++ if (!fname) { ++ hostapd_config_free(newconf); ++ return -1; ++ } ++ hostapd_remove_iface(interfaces, hapd->conf->iface); ++ iface = hostapd_init(interfaces, fname); ++ os_free(fname); ++ hostapd_config_free(newconf); ++ if (!iface) { ++ wpa_printf(MSG_ERROR, ++ "Failed to initialize interface on config reload"); ++ return -1; ++ } ++ iface->interfaces = interfaces; ++ interfaces->iface[interfaces->count] = iface; ++ interfaces->count++; ++ res = hostapd_enable_iface(iface); ++ if (res < 0) ++ wpa_printf(MSG_ERROR, ++ "Failed to enable interface on config reload"); ++ return res; ++ } + iface->conf = newconf; + + for (j = 0; j < iface->num_bss; j++) { +@@ -203,10 +277,12 @@ + + + static void hostapd_broadcast_key_clear_iface(struct hostapd_data *hapd, +- char *ifname) ++ const char *ifname) + { + int i; + ++ if (!ifname || !hapd->drv_priv) ++ return; + for (i = 0; i < NUM_WEP_KEYS; i++) { + if (hostapd_drv_set_key(ifname, hapd, WPA_ALG_NONE, NULL, i, + 0, NULL, 0, NULL, 0)) { +@@ -272,10 +348,11 @@ + + if (!hapd->started) { + wpa_printf(MSG_ERROR, "%s: Interface %s wasn't started", +- __func__, hapd->conf->iface); ++ __func__, hapd->conf ? hapd->conf->iface : "N/A"); + return; + } + hapd->started = 0; ++ hapd->beacon_set_done = 0; + + wpa_printf(MSG_DEBUG, "%s(%s)", __func__, hapd->conf->iface); + iapp_deinit(hapd->iapp); +@@ -292,6 +369,10 @@ + #endif /* CONFIG_NO_RADIUS */ + + hostapd_deinit_wps(hapd); ++#ifdef CONFIG_DPP ++ hostapd_dpp_deinit(hapd); ++ gas_query_ap_deinit(hapd->gas); ++#endif /* CONFIG_DPP */ + + authsrv_deinit(hapd); + +@@ -334,6 +415,23 @@ + wpabuf_free(hapd->mesh_pending_auth); + hapd->mesh_pending_auth = NULL; + #endif /* CONFIG_MESH */ ++ ++ hostapd_clean_rrm(hapd); ++ fils_hlp_deinit(hapd); ++ ++#ifdef CONFIG_SAE ++ { ++ struct hostapd_sae_commit_queue *q; ++ ++ while ((q = dl_list_first(&hapd->sae_commit_queue, ++ struct hostapd_sae_commit_queue, ++ list))) { ++ dl_list_del(&q->list); ++ os_free(q); ++ } ++ } ++ eloop_cancel_timeout(auth_sae_process_commit, hapd, NULL); ++#endif /* CONFIG_SAE */ + } + + +@@ -348,10 +446,12 @@ + static void hostapd_cleanup(struct hostapd_data *hapd) + { + wpa_printf(MSG_DEBUG, "%s(hapd=%p (%s))", __func__, hapd, +- hapd->conf->iface); ++ hapd->conf ? hapd->conf->iface : "N/A"); + if (hapd->iface->interfaces && +- hapd->iface->interfaces->ctrl_iface_deinit) ++ hapd->iface->interfaces->ctrl_iface_deinit) { ++ wpa_msg(hapd->msg_ctx, MSG_INFO, WPA_EVENT_TERMINATING); + hapd->iface->interfaces->ctrl_iface_deinit(hapd); ++ } + hostapd_free_hapd_data(hapd); + } + +@@ -367,7 +467,7 @@ + list))) { + dl_list_del(&info->list); + iface->num_sta_seen--; +- os_free(info); ++ sta_track_del(info); + } + } + +@@ -380,8 +480,11 @@ + hostapd_stop_setup_timers(iface); + #endif /* NEED_AP_MLME */ + #endif /* CONFIG_IEEE80211N */ ++ if (iface->current_mode) ++ acs_cleanup(iface); + hostapd_free_hw_features(iface->hw_features, iface->num_hw_features); + iface->hw_features = NULL; ++ iface->current_mode = NULL; + os_free(iface->current_rates); + iface->current_rates = NULL; + os_free(iface->basic_rates); +@@ -402,6 +505,8 @@ + { + wpa_printf(MSG_DEBUG, "%s(%p)", __func__, iface); + eloop_cancel_timeout(channel_list_update_timeout, iface, NULL); ++ eloop_cancel_timeout(hostapd_interface_setup_failure_handler, iface, ++ NULL); + + hostapd_cleanup_iface_partial(iface); + hostapd_config_free(iface->conf); +@@ -416,7 +521,7 @@ + + static void hostapd_clear_wep(struct hostapd_data *hapd) + { +- if (hapd->drv_priv && !hapd->iface->driver_ap_teardown) { ++ if (hapd->drv_priv && !hapd->iface->driver_ap_teardown && hapd->conf) { + hostapd_set_privacy(hapd, 0); + hostapd_broadcast_wep_clear(hapd); + } +@@ -477,9 +582,12 @@ + ret = -1; + } + } +- wpa_dbg(hapd->msg_ctx, MSG_DEBUG, "Deauthenticate all stations"); +- os_memset(addr, 0xff, ETH_ALEN); +- hostapd_drv_sta_deauth(hapd, addr, reason); ++ if (hapd->conf && hapd->conf->broadcast_deauth) { ++ wpa_dbg(hapd->msg_ctx, MSG_DEBUG, ++ "Deauthenticate all stations"); ++ os_memset(addr, 0xff, ETH_ALEN); ++ hostapd_drv_sta_deauth(hapd, addr, reason); ++ } + hostapd_free_stas(hapd); + + return ret; +@@ -511,6 +619,9 @@ + if (hostapd_drv_none(hapd)) + return 0; + ++ if (iface->conf->use_driver_iface_addr) ++ return 0; ++ + /* Generate BSSID mask that is large enough to cover the BSSIDs. */ + + /* Determine the bits necessary to cover the number of BSSIDs. */ +@@ -520,7 +631,7 @@ + /* Determine the bits necessary to any configured BSSIDs, + if they are higher than the number of BSSIDs. */ + for (j = 0; j < iface->conf->num_bss; j++) { +- if (hostapd_mac_comp_empty(iface->conf->bss[j]->bssid) == 0) { ++ if (is_zero_ether_addr(iface->conf->bss[j]->bssid)) { + if (j) + auto_addr++; + continue; +@@ -562,8 +673,10 @@ + for (i = 5; i > 5 - j; i--) + mask[i] = 0; + j = bits % 8; +- while (j--) ++ while (j) { ++ j--; + mask[i] <<= 1; ++ } + + skip_mask_ext: + wpa_printf(MSG_DEBUG, "BSS count %lu, BSSID mask " MACSTR " (%d bits)", +@@ -672,7 +785,7 @@ + + if (attr->acct_session_id) { + num_attr++; +- if (attr->acct_session_id_len != 17) { ++ if (attr->acct_session_id_len != 16) { + wpa_printf(MSG_DEBUG, + "RADIUS DAS: Acct-Session-Id cannot match"); + return NULL; +@@ -682,10 +795,9 @@ + for (sta = hapd->sta_list; sta; sta = sta->next) { + if (!sta->radius_das_match) + continue; +- os_snprintf(buf, sizeof(buf), "%08X-%08X", +- sta->acct_session_id_hi, +- sta->acct_session_id_lo); +- if (os_memcmp(attr->acct_session_id, buf, 17) != 0) ++ os_snprintf(buf, sizeof(buf), "%016llX", ++ (unsigned long long) sta->acct_session_id); ++ if (os_memcmp(attr->acct_session_id, buf, 16) != 0) + sta->radius_das_match = 0; + else + count++; +@@ -701,7 +813,7 @@ + + if (attr->acct_multi_session_id) { + num_attr++; +- if (attr->acct_multi_session_id_len != 17) { ++ if (attr->acct_multi_session_id_len != 16) { + wpa_printf(MSG_DEBUG, + "RADIUS DAS: Acct-Multi-Session-Id cannot match"); + return NULL; +@@ -712,14 +824,14 @@ + if (!sta->radius_das_match) + continue; + if (!sta->eapol_sm || +- !sta->eapol_sm->acct_multi_session_id_hi) { ++ !sta->eapol_sm->acct_multi_session_id) { + sta->radius_das_match = 0; + continue; + } +- os_snprintf(buf, sizeof(buf), "%08X+%08X", +- sta->eapol_sm->acct_multi_session_id_hi, +- sta->eapol_sm->acct_multi_session_id_lo); +- if (os_memcmp(attr->acct_multi_session_id, buf, 17) != ++ os_snprintf(buf, sizeof(buf), "%016llX", ++ (unsigned long long) ++ sta->eapol_sm->acct_multi_session_id); ++ if (os_memcmp(attr->acct_multi_session_id, buf, 16) != + 0) + sta->radius_das_match = 0; + else +@@ -864,6 +976,48 @@ + return RADIUS_DAS_SUCCESS; + } + ++ ++#ifdef CONFIG_HS20 ++static enum radius_das_res ++hostapd_das_coa(void *ctx, struct radius_das_attrs *attr) ++{ ++ struct hostapd_data *hapd = ctx; ++ struct sta_info *sta; ++ int multi; ++ ++ if (hostapd_das_nas_mismatch(hapd, attr)) ++ return RADIUS_DAS_NAS_MISMATCH; ++ ++ sta = hostapd_das_find_sta(hapd, attr, &multi); ++ if (!sta) { ++ if (multi) { ++ wpa_printf(MSG_DEBUG, ++ "RADIUS DAS: Multiple sessions match - not supported"); ++ return RADIUS_DAS_MULTI_SESSION_MATCH; ++ } ++ wpa_printf(MSG_DEBUG, "RADIUS DAS: No matching session found"); ++ return RADIUS_DAS_SESSION_NOT_FOUND; ++ } ++ ++ wpa_printf(MSG_DEBUG, "RADIUS DAS: Found a matching session " MACSTR ++ " - CoA", MAC2STR(sta->addr)); ++ ++ if (attr->hs20_t_c_filtering) { ++ if (attr->hs20_t_c_filtering[0] & BIT(0)) { ++ wpa_printf(MSG_DEBUG, ++ "HS 2.0: Unexpected Terms and Conditions filtering required in CoA-Request"); ++ return RADIUS_DAS_COA_FAILED; ++ } ++ ++ hs20_t_c_filtering(hapd, sta, 0); ++ } ++ ++ return RADIUS_DAS_SUCCESS; ++} ++#else /* CONFIG_HS20 */ ++#define hostapd_das_coa NULL ++#endif /* CONFIG_HS20 */ ++ + #endif /* CONFIG_NO_RADIUS */ + + +@@ -905,12 +1059,9 @@ + hapd->started = 1; + + if (!first || first == -1) { +- if (hostapd_mac_comp_empty(conf->bssid) == 0) { +- /* Allocate the next available BSSID. */ +- do { +- inc_byte_array(hapd->own_addr, ETH_ALEN); +- } while (mac_in_conf(hapd->iconf, hapd->own_addr)); +- } else { ++ u8 *addr = hapd->own_addr; ++ ++ if (!is_zero_ether_addr(conf->bssid)) { + /* Allocate the configured BSSID. */ + os_memcpy(hapd->own_addr, conf->bssid, ETH_ALEN); + +@@ -922,11 +1073,18 @@ + "the radio", conf->iface); + return -1; + } ++ } else if (hapd->iconf->use_driver_iface_addr) { ++ addr = NULL; ++ } else { ++ /* Allocate the next available BSSID. */ ++ do { ++ inc_byte_array(hapd->own_addr, ETH_ALEN); ++ } while (mac_in_conf(hapd->iconf, hapd->own_addr)); + } + + hapd->interface_added = 1; + if (hostapd_if_add(hapd->iface->bss[0], WPA_IF_AP_BSS, +- conf->iface, hapd->own_addr, hapd, ++ conf->iface, addr, hapd, + &hapd->drv_priv, force_ifname, if_addr, + conf->bridge[0] ? conf->bridge : NULL, + first == -1)) { +@@ -935,13 +1093,21 @@ + hapd->interface_added = 0; + return -1; + } ++ ++ if (!addr) ++ os_memcpy(hapd->own_addr, if_addr, ETH_ALEN); + } + + if (conf->wmm_enabled < 0) + conf->wmm_enabled = hapd->iconf->ieee80211n; + ++#ifdef CONFIG_IEEE80211R_AP ++ if (is_zero_ether_addr(conf->r1_key_holder)) ++ os_memcpy(conf->r1_key_holder, hapd->own_addr, ETH_ALEN); ++#endif /* CONFIG_IEEE80211R_AP */ ++ + #ifdef CONFIG_MESH +- if (hapd->iface->mconf == NULL) ++ if ((hapd->conf->mesh & MESH_ENABLED) && hapd->iface->mconf == NULL) + flush_old_stations = 0; + #endif /* CONFIG_MESH */ + +@@ -1022,8 +1188,11 @@ + das_conf.time_window = conf->radius_das_time_window; + das_conf.require_event_timestamp = + conf->radius_das_require_event_timestamp; ++ das_conf.require_message_authenticator = ++ conf->radius_das_require_message_authenticator; + das_conf.ctx = hapd; + das_conf.disconnect = hostapd_das_disconnect; ++ das_conf.coa = hostapd_das_coa; + hapd->radius_das = radius_das_init(&das_conf); + if (hapd->radius_das == NULL) { + wpa_printf(MSG_ERROR, "RADIUS DAS initialization " +@@ -1040,6 +1209,14 @@ + if (hostapd_init_wps(hapd, conf)) + return -1; + ++#ifdef CONFIG_DPP ++ hapd->gas = gas_query_ap_init(hapd, hapd->msg_ctx); ++ if (!hapd->gas) ++ return -1; ++ if (hostapd_dpp_init(hapd)) ++ return -1; ++#endif /* CONFIG_DPP */ ++ + if (authsrv_init(hapd) < 0) + return -1; + +@@ -1127,7 +1304,7 @@ + struct hostapd_tx_queue_params *p; + + #ifdef CONFIG_MESH +- if (iface->mconf == NULL) ++ if ((hapd->conf->mesh & MESH_ENABLED) && iface->mconf == NULL) + return; + #endif /* CONFIG_MESH */ + +@@ -1508,17 +1685,119 @@ + + #endif /* CONFIG_FST */ + ++#ifdef CONFIG_OWE + +-/** +- * hostapd_setup_interface_complete - Complete interface setup +- * +- * This function is called when previous steps in the interface setup has been +- * completed. This can also start operations, e.g., DFS, that will require +- * additional processing before interface is ready to be enabled. Such +- * operations will call this function from eloop callbacks when finished. +- */ +-int hostapd_setup_interface_complete(struct hostapd_iface *iface, int err) ++static int hostapd_owe_iface_iter(struct hostapd_iface *iface, void *ctx) + { ++ struct hostapd_data *hapd = ctx; ++ size_t i; ++ ++ for (i = 0; i < iface->num_bss; i++) { ++ struct hostapd_data *bss = iface->bss[i]; ++ ++ if (os_strcmp(hapd->conf->owe_transition_ifname, ++ bss->conf->iface) != 0) ++ continue; ++ ++ wpa_printf(MSG_DEBUG, ++ "OWE: ifname=%s found transition mode ifname=%s BSSID " ++ MACSTR " SSID %s", ++ hapd->conf->iface, bss->conf->iface, ++ MAC2STR(bss->own_addr), ++ wpa_ssid_txt(bss->conf->ssid.ssid, ++ bss->conf->ssid.ssid_len)); ++ if (!bss->conf->ssid.ssid_set || !bss->conf->ssid.ssid_len || ++ is_zero_ether_addr(bss->own_addr)) ++ continue; ++ ++ os_memcpy(hapd->conf->owe_transition_bssid, bss->own_addr, ++ ETH_ALEN); ++ os_memcpy(hapd->conf->owe_transition_ssid, ++ bss->conf->ssid.ssid, bss->conf->ssid.ssid_len); ++ hapd->conf->owe_transition_ssid_len = bss->conf->ssid.ssid_len; ++ wpa_printf(MSG_DEBUG, ++ "OWE: Copied transition mode information"); ++ return 1; ++ } ++ ++ return 0; ++} ++ ++ ++int hostapd_owe_trans_get_info(struct hostapd_data *hapd) ++{ ++ if (hapd->conf->owe_transition_ssid_len > 0 && ++ !is_zero_ether_addr(hapd->conf->owe_transition_bssid)) ++ return 0; ++ ++ /* Find transition mode SSID/BSSID information from a BSS operated by ++ * this hostapd instance. */ ++ if (!hapd->iface->interfaces || ++ !hapd->iface->interfaces->for_each_interface) ++ return hostapd_owe_iface_iter(hapd->iface, hapd); ++ else ++ return hapd->iface->interfaces->for_each_interface( ++ hapd->iface->interfaces, hostapd_owe_iface_iter, hapd); ++} ++ ++ ++static int hostapd_owe_iface_iter2(struct hostapd_iface *iface, void *ctx) ++{ ++ size_t i; ++ ++ for (i = 0; i < iface->num_bss; i++) { ++ struct hostapd_data *bss = iface->bss[i]; ++ int res; ++ ++ if (!bss->conf->owe_transition_ifname[0]) ++ continue; ++ res = hostapd_owe_trans_get_info(bss); ++ if (res == 0) ++ continue; ++ wpa_printf(MSG_DEBUG, ++ "OWE: Matching transition mode interface enabled - update beacon data for %s", ++ bss->conf->iface); ++ ieee802_11_set_beacon(bss); ++ } ++ ++ return 0; ++} ++ ++#endif /* CONFIG_OWE */ ++ ++ ++static void hostapd_owe_update_trans(struct hostapd_iface *iface) ++{ ++#ifdef CONFIG_OWE ++ /* Check whether the enabled BSS can complete OWE transition mode ++ * configuration for any pending interface. */ ++ if (!iface->interfaces || ++ !iface->interfaces->for_each_interface) ++ hostapd_owe_iface_iter2(iface, NULL); ++ else ++ iface->interfaces->for_each_interface( ++ iface->interfaces, hostapd_owe_iface_iter2, NULL); ++#endif /* CONFIG_OWE */ ++} ++ ++ ++static void hostapd_interface_setup_failure_handler(void *eloop_ctx, ++ void *timeout_ctx) ++{ ++ struct hostapd_iface *iface = eloop_ctx; ++ struct hostapd_data *hapd; ++ ++ if (iface->num_bss < 1 || !iface->bss || !iface->bss[0]) ++ return; ++ hapd = iface->bss[0]; ++ if (hapd->setup_complete_cb) ++ hapd->setup_complete_cb(hapd->setup_complete_cb_ctx); ++} ++ ++ ++static int hostapd_setup_interface_complete_sync(struct hostapd_iface *iface, ++ int err) ++{ + struct hostapd_data *hapd = iface->bss[0]; + size_t j; + u8 *prev_addr; +@@ -1605,15 +1884,17 @@ + } + } + +- if (hapd->iconf->rts_threshold > -1 && +- hostapd_set_rts(hapd, hapd->iconf->rts_threshold)) { ++ if (hapd->iconf->rts_threshold >= -1 && ++ hostapd_set_rts(hapd, hapd->iconf->rts_threshold) && ++ hapd->iconf->rts_threshold >= -1) { + wpa_printf(MSG_ERROR, "Could not set RTS threshold for " + "kernel driver"); + goto fail; + } + +- if (hapd->iconf->fragm_threshold > -1 && +- hostapd_set_frag(hapd, hapd->iconf->fragm_threshold)) { ++ if (hapd->iconf->fragm_threshold >= -1 && ++ hostapd_set_frag(hapd, hapd->iconf->fragm_threshold) && ++ hapd->iconf->fragm_threshold != -1) { + wpa_printf(MSG_ERROR, "Could not set fragmentation threshold " + "for kernel driver"); + goto fail; +@@ -1626,14 +1907,17 @@ + if (j) + os_memcpy(hapd->own_addr, prev_addr, ETH_ALEN); + if (hostapd_setup_bss(hapd, j == 0)) { +- do { ++ for (;;) { + hapd = iface->bss[j]; + hostapd_bss_deinit_no_free(hapd); + hostapd_free_hapd_data(hapd); +- } while (j-- > 0); ++ if (j == 0) ++ break; ++ j--; ++ } + goto fail; + } +- if (hostapd_mac_comp_empty(hapd->conf->bssid) == 0) ++ if (is_zero_ether_addr(hapd->conf->bssid)) + prev_addr = hapd->own_addr; + } + hapd = iface->bss[0]; +@@ -1641,7 +1925,6 @@ + hostapd_tx_queue_params(iface); + + ap_list_init(iface); +- dl_list_init(&iface->sta_seen); + + hostapd_set_acl(hapd); + +@@ -1692,6 +1975,7 @@ + #endif /* CONFIG_FST */ + + hostapd_set_state(iface, HAPD_IFACE_ENABLED); ++ hostapd_owe_update_trans(iface); + wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, AP_EVENT_ENABLED); + if (hapd->setup_complete_cb) + hapd->setup_complete_cb(hapd->setup_complete_cb_ctx); +@@ -1701,6 +1985,9 @@ + if (iface->interfaces && iface->interfaces->terminate_on_error > 0) + iface->interfaces->terminate_on_error--; + ++ for (j = 0; j < iface->num_bss; j++) ++ hostapd_neighbor_set_own_report(iface->bss[j]); ++ + return 0; + + fail: +@@ -1713,13 +2000,107 @@ + iface->fst = NULL; + } + #endif /* CONFIG_FST */ +- if (iface->interfaces && iface->interfaces->terminate_on_error) ++ ++ if (iface->interfaces && iface->interfaces->terminate_on_error) { + eloop_terminate(); ++ } else if (hapd->setup_complete_cb) { ++ /* ++ * Calling hapd->setup_complete_cb directly may cause iface ++ * deinitialization which may be accessed later by the caller. ++ */ ++ eloop_register_timeout(0, 0, ++ hostapd_interface_setup_failure_handler, ++ iface, NULL); ++ } ++ + return -1; + } + + + /** ++ * hostapd_setup_interface_complete - Complete interface setup ++ * ++ * This function is called when previous steps in the interface setup has been ++ * completed. This can also start operations, e.g., DFS, that will require ++ * additional processing before interface is ready to be enabled. Such ++ * operations will call this function from eloop callbacks when finished. ++ */ ++int hostapd_setup_interface_complete(struct hostapd_iface *iface, int err) ++{ ++ struct hapd_interfaces *interfaces = iface->interfaces; ++ struct hostapd_data *hapd = iface->bss[0]; ++ unsigned int i; ++ int not_ready_in_sync_ifaces = 0; ++ ++ if (!iface->need_to_start_in_sync) ++ return hostapd_setup_interface_complete_sync(iface, err); ++ ++ if (err) { ++ wpa_printf(MSG_ERROR, "Interface initialization failed"); ++ hostapd_set_state(iface, HAPD_IFACE_DISABLED); ++ iface->need_to_start_in_sync = 0; ++ wpa_msg(hapd->msg_ctx, MSG_INFO, AP_EVENT_DISABLED); ++ if (interfaces && interfaces->terminate_on_error) ++ eloop_terminate(); ++ return -1; ++ } ++ ++ if (iface->ready_to_start_in_sync) { ++ /* Already in ready and waiting. should never happpen */ ++ return 0; ++ } ++ ++ for (i = 0; i < interfaces->count; i++) { ++ if (interfaces->iface[i]->need_to_start_in_sync && ++ !interfaces->iface[i]->ready_to_start_in_sync) ++ not_ready_in_sync_ifaces++; ++ } ++ ++ /* ++ * Check if this is the last interface, if yes then start all the other ++ * waiting interfaces. If not, add this interface to the waiting list. ++ */ ++ if (not_ready_in_sync_ifaces > 1 && iface->state == HAPD_IFACE_DFS) { ++ /* ++ * If this interface went through CAC, do not synchronize, just ++ * start immediately. ++ */ ++ iface->need_to_start_in_sync = 0; ++ wpa_printf(MSG_INFO, ++ "%s: Finished CAC - bypass sync and start interface", ++ iface->bss[0]->conf->iface); ++ return hostapd_setup_interface_complete_sync(iface, err); ++ } ++ ++ if (not_ready_in_sync_ifaces > 1) { ++ /* need to wait as there are other interfaces still coming up */ ++ iface->ready_to_start_in_sync = 1; ++ wpa_printf(MSG_INFO, ++ "%s: Interface waiting to sync with other interfaces", ++ iface->bss[0]->conf->iface); ++ return 0; ++ } ++ ++ wpa_printf(MSG_INFO, ++ "%s: Last interface to sync - starting all interfaces", ++ iface->bss[0]->conf->iface); ++ iface->need_to_start_in_sync = 0; ++ hostapd_setup_interface_complete_sync(iface, err); ++ for (i = 0; i < interfaces->count; i++) { ++ if (interfaces->iface[i]->need_to_start_in_sync && ++ interfaces->iface[i]->ready_to_start_in_sync) { ++ hostapd_setup_interface_complete_sync( ++ interfaces->iface[i], 0); ++ /* Only once the interfaces are sync started */ ++ interfaces->iface[i]->need_to_start_in_sync = 0; ++ } ++ } ++ ++ return 0; ++} ++ ++ ++/** + * hostapd_setup_interface - Setup of an interface + * @iface: Pointer to interface data. + * Returns: 0 on success, -1 on failure +@@ -1776,8 +2157,19 @@ + hapd->iconf = conf; + hapd->conf = bss; + hapd->iface = hapd_iface; +- hapd->driver = hapd->iconf->driver; ++ if (conf) ++ hapd->driver = conf->driver; + hapd->ctrl_sock = -1; ++ dl_list_init(&hapd->ctrl_dst); ++ dl_list_init(&hapd->nr_db); ++ hapd->dhcp_sock = -1; ++#ifdef CONFIG_IEEE80211R_AP ++ dl_list_init(&hapd->l2_queue); ++ dl_list_init(&hapd->l2_oui_queue); ++#endif /* CONFIG_IEEE80211R_AP */ ++#ifdef CONFIG_SAE ++ dl_list_init(&hapd->sae_commit_queue); ++#endif /* CONFIG_SAE */ + + return hapd; + } +@@ -1785,8 +2177,10 @@ + + static void hostapd_bss_deinit(struct hostapd_data *hapd) + { ++ if (!hapd) ++ return; + wpa_printf(MSG_DEBUG, "%s: deinit bss %s", __func__, +- hapd->conf->iface); ++ hapd->conf ? hapd->conf->iface : "N/A"); + hostapd_bss_deinit_no_free(hapd); + wpa_msg(hapd->msg_ctx, MSG_INFO, AP_EVENT_DISABLED); + hostapd_cleanup(hapd); +@@ -1803,12 +2197,6 @@ + + hostapd_set_state(iface, HAPD_IFACE_DISABLED); + +-#ifdef CONFIG_IEEE80211N +-#ifdef NEED_AP_MLME +- hostapd_stop_setup_timers(iface); +- eloop_cancel_timeout(ap_ht2040_timeout, iface, NULL); +-#endif /* NEED_AP_MLME */ +-#endif /* CONFIG_IEEE80211N */ + eloop_cancel_timeout(channel_list_update_timeout, iface, NULL); + iface->wait_channel_update = 0; + +@@ -1819,8 +2207,18 @@ + } + #endif /* CONFIG_FST */ + +- for (j = iface->num_bss - 1; j >= 0; j--) ++ for (j = (int) iface->num_bss - 1; j >= 0; j--) { ++ if (!iface->bss) ++ break; + hostapd_bss_deinit(iface->bss[j]); ++ } ++ ++#ifdef CONFIG_IEEE80211N ++#ifdef NEED_AP_MLME ++ hostapd_stop_setup_timers(iface); ++ eloop_cancel_timeout(ap_ht2040_timeout, iface, NULL); ++#endif /* NEED_AP_MLME */ ++#endif /* CONFIG_IEEE80211N */ + } + + +@@ -1829,6 +2227,8 @@ + size_t j; + wpa_printf(MSG_DEBUG, "%s(%p)", __func__, iface); + for (j = 0; j < iface->num_bss; j++) { ++ if (!iface->bss) ++ break; + wpa_printf(MSG_DEBUG, "%s: free hapd %p", + __func__, iface->bss[j]); + os_free(iface->bss[j]); +@@ -1837,6 +2237,20 @@ + } + + ++struct hostapd_iface * hostapd_alloc_iface(void) ++{ ++ struct hostapd_iface *hapd_iface; ++ ++ hapd_iface = os_zalloc(sizeof(*hapd_iface)); ++ if (!hapd_iface) ++ return NULL; ++ ++ dl_list_init(&hapd_iface->sta_seen); ++ ++ return hapd_iface; ++} ++ ++ + /** + * hostapd_init - Allocate and initialize per-interface data + * @config_file: Path to the configuration file +@@ -1854,7 +2268,7 @@ + struct hostapd_data *hapd; + size_t i; + +- hapd_iface = os_zalloc(sizeof(*hapd_iface)); ++ hapd_iface = hostapd_alloc_iface(); + if (hapd_iface == NULL) + goto fail; + +@@ -2158,6 +2572,11 @@ + !!(hapd_iface->drv_flags & + WPA_DRIVER_FLAGS_AP_TEARDOWN_SUPPORT); + ++#ifdef NEED_AP_MLME ++ for (j = 0; j < hapd_iface->num_bss; j++) ++ hostapd_cleanup_cs_params(hapd_iface->bss[j]); ++#endif /* NEED_AP_MLME */ ++ + /* same as hostapd_interface_deinit without deinitializing ctrl-iface */ + for (j = 0; j < hapd_iface->num_bss; j++) { + struct hostapd_data *hapd = hapd_iface->bss[j]; +@@ -2190,7 +2609,7 @@ + return NULL; + interfaces->iface = iface; + hapd_iface = interfaces->iface[interfaces->count] = +- os_zalloc(sizeof(*hapd_iface)); ++ hostapd_alloc_iface(); + if (hapd_iface == NULL) { + wpa_printf(MSG_ERROR, "%s: Failed to allocate memory for " + "the interface", __func__); +@@ -2215,7 +2634,7 @@ + if (conf == NULL) { + wpa_printf(MSG_ERROR, "%s: Failed to allocate memory for " + "configuration", __func__); +- return NULL; ++ return NULL; + } + + if (driver) { +@@ -2368,6 +2787,7 @@ + return -1; + } + } ++ hostapd_owe_update_trans(hapd_iface); + return 0; + } + +@@ -2557,6 +2977,7 @@ + } + + hostapd_prune_associations(hapd, sta->addr); ++ ap_sta_clear_disconnect_timeouts(hapd, sta); + + /* IEEE 802.11F (IAPP) */ + if (hapd->conf->ieee802_11f) +@@ -2584,15 +3005,28 @@ + ieee802_1x_new_station(hapd, sta); + if (reassoc) { + if (sta->auth_alg != WLAN_AUTH_FT && ++ sta->auth_alg != WLAN_AUTH_FILS_SK && ++ sta->auth_alg != WLAN_AUTH_FILS_SK_PFS && ++ sta->auth_alg != WLAN_AUTH_FILS_PK && + !(sta->flags & (WLAN_STA_WPS | WLAN_STA_MAYBE_WPS))) + wpa_auth_sm_event(sta->wpa_sm, WPA_REAUTH); + } else + wpa_auth_sta_associated(hapd->wpa_auth, sta->wpa_sm); + +- if (!(hapd->iface->drv_flags & WPA_DRIVER_FLAGS_INACTIVITY_TIMER)) { +- wpa_printf(MSG_DEBUG, "%s: reschedule ap_handle_timer timeout " +- "for " MACSTR " (%d seconds - ap_max_inactivity)", +- __func__, MAC2STR(sta->addr), ++ if (hapd->iface->drv_flags & WPA_DRIVER_FLAGS_WIRED) { ++ if (eloop_cancel_timeout(ap_handle_timer, hapd, sta) > 0) { ++ wpa_printf(MSG_DEBUG, ++ "%s: %s: canceled wired ap_handle_timer timeout for " ++ MACSTR, ++ hapd->conf->iface, __func__, ++ MAC2STR(sta->addr)); ++ } ++ } else if (!(hapd->iface->drv_flags & ++ WPA_DRIVER_FLAGS_INACTIVITY_TIMER)) { ++ wpa_printf(MSG_DEBUG, ++ "%s: %s: reschedule ap_handle_timer timeout for " ++ MACSTR " (%d seconds - ap_max_inactivity)", ++ hapd->conf->iface, __func__, MAC2STR(sta->addr), + hapd->conf->ap_max_inactivity); + eloop_cancel_timeout(ap_handle_timer, hapd, sta); + eloop_register_timeout(hapd->conf->ap_max_inactivity, 0, +@@ -2627,12 +3061,23 @@ + void hostapd_set_state(struct hostapd_iface *iface, enum hostapd_iface_state s) + { + wpa_printf(MSG_INFO, "%s: interface state %s->%s", +- iface->conf->bss[0]->iface, hostapd_state_text(iface->state), +- hostapd_state_text(s)); ++ iface->conf ? iface->conf->bss[0]->iface : "N/A", ++ hostapd_state_text(iface->state), hostapd_state_text(s)); + iface->state = s; + } + + ++int hostapd_csa_in_progress(struct hostapd_iface *iface) ++{ ++ unsigned int i; ++ ++ for (i = 0; i < iface->num_bss; i++) ++ if (iface->bss[i]->csa_in_progress) ++ return 1; ++ return 0; ++} ++ ++ + #ifdef NEED_AP_MLME + + static void free_beacon_data(struct beacon_data *beacon) +@@ -2671,60 +3116,52 @@ + goto free_ap_params; + + ret = -1; +- beacon->head = os_malloc(params.head_len); ++ beacon->head = os_memdup(params.head, params.head_len); + if (!beacon->head) + goto free_ap_extra_ies; + +- os_memcpy(beacon->head, params.head, params.head_len); + beacon->head_len = params.head_len; + +- beacon->tail = os_malloc(params.tail_len); ++ beacon->tail = os_memdup(params.tail, params.tail_len); + if (!beacon->tail) + goto free_beacon; + +- os_memcpy(beacon->tail, params.tail, params.tail_len); + beacon->tail_len = params.tail_len; + + if (params.proberesp != NULL) { +- beacon->probe_resp = os_malloc(params.proberesp_len); ++ beacon->probe_resp = os_memdup(params.proberesp, ++ params.proberesp_len); + if (!beacon->probe_resp) + goto free_beacon; + +- os_memcpy(beacon->probe_resp, params.proberesp, +- params.proberesp_len); + beacon->probe_resp_len = params.proberesp_len; + } + + /* copy the extra ies */ + if (beacon_extra) { +- beacon->beacon_ies = os_malloc(wpabuf_len(beacon_extra)); ++ beacon->beacon_ies = os_memdup(beacon_extra->buf, ++ wpabuf_len(beacon_extra)); + if (!beacon->beacon_ies) + goto free_beacon; + +- os_memcpy(beacon->beacon_ies, +- beacon_extra->buf, wpabuf_len(beacon_extra)); + beacon->beacon_ies_len = wpabuf_len(beacon_extra); + } + + if (proberesp_extra) { +- beacon->proberesp_ies = +- os_malloc(wpabuf_len(proberesp_extra)); ++ beacon->proberesp_ies = os_memdup(proberesp_extra->buf, ++ wpabuf_len(proberesp_extra)); + if (!beacon->proberesp_ies) + goto free_beacon; + +- os_memcpy(beacon->proberesp_ies, proberesp_extra->buf, +- wpabuf_len(proberesp_extra)); + beacon->proberesp_ies_len = wpabuf_len(proberesp_extra); + } + + if (assocresp_extra) { +- beacon->assocresp_ies = +- os_malloc(wpabuf_len(assocresp_extra)); ++ beacon->assocresp_ies = os_memdup(assocresp_extra->buf, ++ wpabuf_len(assocresp_extra)); + if (!beacon->assocresp_ies) + goto free_beacon; + +- os_memcpy(beacon->assocresp_ies, assocresp_extra->buf, +- wpabuf_len(assocresp_extra)); + beacon->assocresp_ies_len = wpabuf_len(assocresp_extra); + } + +@@ -2744,9 +3181,9 @@ + + + /* +- * TODO: This flow currently supports only changing frequency within the +- * same hw_mode. Any other changes to MAC parameters or provided settings (even +- * width) are not supported. ++ * TODO: This flow currently supports only changing channel and width within ++ * the same hw_mode. Any other changes to MAC parameters or provided settings ++ * are not supported. + */ + static int hostapd_change_config_freq(struct hostapd_data *hapd, + struct hostapd_config *conf, +@@ -2765,15 +3202,44 @@ + return -1; + + /* if a pointer to old_params is provided we save previous state */ +- if (old_params) { +- old_params->channel = conf->channel; +- old_params->ht_enabled = conf->ieee80211n; +- old_params->sec_channel_offset = conf->secondary_channel; ++ if (old_params && ++ hostapd_set_freq_params(old_params, conf->hw_mode, ++ hostapd_hw_get_freq(hapd, conf->channel), ++ conf->channel, conf->ieee80211n, ++ conf->ieee80211ac, ++ conf->secondary_channel, ++ conf->vht_oper_chwidth, ++ conf->vht_oper_centr_freq_seg0_idx, ++ conf->vht_oper_centr_freq_seg1_idx, ++ conf->vht_capab)) ++ return -1; ++ ++ switch (params->bandwidth) { ++ case 0: ++ case 20: ++ case 40: ++ conf->vht_oper_chwidth = VHT_CHANWIDTH_USE_HT; ++ break; ++ case 80: ++ if (params->center_freq2) ++ conf->vht_oper_chwidth = VHT_CHANWIDTH_80P80MHZ; ++ else ++ conf->vht_oper_chwidth = VHT_CHANWIDTH_80MHZ; ++ break; ++ case 160: ++ conf->vht_oper_chwidth = VHT_CHANWIDTH_160MHZ; ++ break; ++ default: ++ return -1; + } + + conf->channel = channel; + conf->ieee80211n = params->ht_enabled; + conf->secondary_channel = params->sec_channel_offset; ++ ieee80211_freq_to_chan(params->center_freq1, ++ &conf->vht_oper_centr_freq_seg0_idx); ++ ieee80211_freq_to_chan(params->center_freq2, ++ &conf->vht_oper_centr_freq_seg1_idx); + + /* TODO: maybe call here hostapd_config_check here? */ + +@@ -2787,11 +3253,43 @@ + struct hostapd_iface *iface = hapd->iface; + struct hostapd_freq_params old_freq; + int ret; ++ u8 chan, vht_bandwidth; + + os_memset(&old_freq, 0, sizeof(old_freq)); + if (!iface || !iface->freq || hapd->csa_in_progress) + return -1; + ++ switch (settings->freq_params.bandwidth) { ++ case 80: ++ if (settings->freq_params.center_freq2) ++ vht_bandwidth = VHT_CHANWIDTH_80P80MHZ; ++ else ++ vht_bandwidth = VHT_CHANWIDTH_80MHZ; ++ break; ++ case 160: ++ vht_bandwidth = VHT_CHANWIDTH_160MHZ; ++ break; ++ default: ++ vht_bandwidth = VHT_CHANWIDTH_USE_HT; ++ break; ++ } ++ ++ if (ieee80211_freq_to_channel_ext( ++ settings->freq_params.freq, ++ settings->freq_params.sec_channel_offset, ++ vht_bandwidth, ++ &hapd->iface->cs_oper_class, ++ &chan) == NUM_HOSTAPD_MODES) { ++ wpa_printf(MSG_DEBUG, ++ "invalid frequency for channel switch (freq=%d, sec_channel_offset=%d, vht_enabled=%d)", ++ settings->freq_params.freq, ++ settings->freq_params.sec_channel_offset, ++ settings->freq_params.vht_enabled); ++ return -1; ++ } ++ ++ settings->freq_params.channel = chan; ++ + ret = hostapd_change_config_freq(iface->bss[0], iface->conf, + &settings->freq_params, + &old_freq); +@@ -2818,8 +3316,10 @@ + return ret; + } + +- settings->counter_offset_beacon = hapd->cs_c_off_beacon; +- settings->counter_offset_presp = hapd->cs_c_off_proberesp; ++ settings->counter_offset_beacon[0] = hapd->cs_c_off_beacon; ++ settings->counter_offset_presp[0] = hapd->cs_c_off_proberesp; ++ settings->counter_offset_beacon[1] = hapd->cs_c_off_ecsa_beacon; ++ settings->counter_offset_presp[1] = hapd->cs_c_off_ecsa_proberesp; + + return 0; + } +@@ -2833,9 +3333,24 @@ + hapd->cs_c_off_beacon = 0; + hapd->cs_c_off_proberesp = 0; + hapd->csa_in_progress = 0; ++ hapd->cs_c_off_ecsa_beacon = 0; ++ hapd->cs_c_off_ecsa_proberesp = 0; + } + + ++void hostapd_chan_switch_vht_config(struct hostapd_data *hapd, int vht_enabled) ++{ ++ if (vht_enabled) ++ hapd->iconf->ch_switch_vht_config |= CH_SWITCH_VHT_ENABLED; ++ else ++ hapd->iconf->ch_switch_vht_config |= CH_SWITCH_VHT_DISABLED; ++ ++ hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211, ++ HOSTAPD_LEVEL_INFO, "CHAN_SWITCH VHT CONFIG 0x%x", ++ hapd->iconf->ch_switch_vht_config); ++} ++ ++ + int hostapd_switch_channel(struct hostapd_data *hapd, + struct csa_settings *settings) + { +@@ -2870,7 +3385,6 @@ + const struct hostapd_freq_params *freq_params) + { + int vht_seg0_idx = 0, vht_seg1_idx = 0, vht_bw = VHT_CHANWIDTH_USE_HT; +- unsigned int i; + + wpa_printf(MSG_DEBUG, "Restarting all CSA-related BSSes"); + +@@ -2912,15 +3426,15 @@ + /* + * cs_params must not be cleared earlier because the freq_params + * argument may actually point to one of these. ++ * These params will be cleared during interface disable below. + */ +- for (i = 0; i < iface->num_bss; i++) +- hostapd_cleanup_cs_params(iface->bss[i]); +- + hostapd_disable_iface(iface); + hostapd_enable_iface(iface); + } + ++#endif /* NEED_AP_MLME */ + ++ + struct hostapd_data * hostapd_get_iface(struct hapd_interfaces *interfaces, + const char *ifname) + { +@@ -2940,9 +3454,7 @@ + return NULL; + } + +-#endif /* NEED_AP_MLME */ + +- + void hostapd_periodic_iface(struct hostapd_iface *iface) + { + size_t i; +--- contrib/wpa/src/ap/hostapd.h.orig ++++ contrib/wpa/src/ap/hostapd.h +@@ -14,6 +14,13 @@ + #include "ap_config.h" + #include "drivers/driver.h" + ++#define OCE_STA_CFON_ENABLED(hapd) \ ++ ((hapd->conf->oce & OCE_STA_CFON) && \ ++ (hapd->iface->drv_flags & WPA_DRIVER_FLAGS_OCE_STA_CFON)) ++#define OCE_AP_ENABLED(hapd) \ ++ ((hapd->conf->oce & OCE_AP) && \ ++ (hapd->iface->drv_flags & WPA_DRIVER_FLAGS_OCE_AP)) ++ + struct wpa_ctrl_dst; + struct radius_server_data; + struct upnp_wps_device_sm; +@@ -41,7 +48,7 @@ + + size_t count; + int global_ctrl_sock; +- struct wpa_ctrl_dst *global_ctrl_dst; ++ struct dl_list global_ctrl_dst; + char *global_iface_path; + char *global_iface_name; + #ifndef CONFIG_NATIVE_WINDOWS +@@ -53,6 +60,14 @@ + #ifndef CONFIG_NO_VLAN + struct dynamic_iface *vlan_priv; + #endif /* CONFIG_NO_VLAN */ ++#ifdef CONFIG_ETH_P_OUI ++ struct dl_list eth_p_oui; /* OUI Extended EtherType handlers */ ++#endif /* CONFIG_ETH_P_OUI */ ++ int eloop_initialized; ++ ++#ifdef CONFIG_DPP ++ struct dpp_global *dpp; ++#endif /* CONFIG_DPP */ + }; + + enum hostapd_chan_status { +@@ -75,6 +90,7 @@ + }; + + struct hostapd_frame_info { ++ unsigned int freq; + u32 channel; + u32 datarate; + int ssi_signal; /* dBm */ +@@ -99,7 +115,25 @@ + u8 peer_addr[ETH_ALEN]; + }; + ++struct hostapd_neighbor_entry { ++ struct dl_list list; ++ u8 bssid[ETH_ALEN]; ++ struct wpa_ssid_value ssid; ++ struct wpabuf *nr; ++ struct wpabuf *lci; ++ struct wpabuf *civic; ++ /* LCI update time */ ++ struct os_time lci_date; ++ int stationary; ++}; + ++struct hostapd_sae_commit_queue { ++ struct dl_list list; ++ int rssi; ++ size_t len; ++ u8 msg[]; ++}; ++ + /** + * struct hostapd_data - hostapd per-BSS data structure + */ +@@ -138,7 +172,7 @@ + void *msg_ctx_parent; /* parent interface ctx for wpa_msg() calls */ + + struct radius_client_data *radius; +- u32 acct_session_id_hi, acct_session_id_lo; ++ u64 acct_session_id; + struct radius_das_data *radius_das; + + struct iapp_data *iapp; +@@ -155,7 +189,7 @@ + int tkip_countermeasures; + + int ctrl_sock; +- struct wpa_ctrl_dst *ctrl_dst; ++ struct dl_list ctrl_dst; + + void *ssl_ctx; + void *eap_sim_db_priv; +@@ -173,6 +207,17 @@ + #endif /* CONFIG_FULL_DYNAMIC_VLAN */ + + struct l2_packet_data *l2; ++ ++#ifdef CONFIG_IEEE80211R_AP ++ struct dl_list l2_queue; ++ struct dl_list l2_oui_queue; ++ struct eth_p_oui_ctx *oui_pull; ++ struct eth_p_oui_ctx *oui_resp; ++ struct eth_p_oui_ctx *oui_push; ++ struct eth_p_oui_ctx *oui_sreq; ++ struct eth_p_oui_ctx *oui_sresp; ++#endif /* CONFIG_IEEE80211R_AP */ ++ + struct wps_context *wps; + + int beacon_set_done; +@@ -228,10 +273,9 @@ + unsigned int cs_c_off_beacon; + unsigned int cs_c_off_proberesp; + int csa_in_progress; ++ unsigned int cs_c_off_ecsa_beacon; ++ unsigned int cs_c_off_ecsa_proberesp; + +- /* BSS Load */ +- unsigned int bss_load_update_timeout; +- + #ifdef CONFIG_P2P + struct p2p_data *p2p; + struct p2p_group *p2p_group; +@@ -246,9 +290,6 @@ + int noa_start; + int noa_duration; + #endif /* CONFIG_P2P */ +-#ifdef CONFIG_INTERWORKING +- size_t gas_frag_limit; +-#endif /* CONFIG_INTERWORKING */ + #ifdef CONFIG_PROXYARP + struct l2_packet_data *sock_dhcp; + struct l2_packet_data *sock_ndisc; +@@ -256,9 +297,11 @@ + #ifdef CONFIG_MESH + int num_plinks; + int max_plinks; +- void (*mesh_sta_free_cb)(struct sta_info *sta); ++ void (*mesh_sta_free_cb)(struct hostapd_data *hapd, ++ struct sta_info *sta); + struct wpabuf *mesh_pending_auth; + struct os_reltime mesh_pending_auth_time; ++ u8 mesh_required_peer[ETH_ALEN]; + #endif /* CONFIG_MESH */ + + #ifdef CONFIG_SQLITE +@@ -269,7 +312,10 @@ + /** Key used for generating SAE anti-clogging tokens */ + u8 sae_token_key[8]; + struct os_reltime last_sae_token_key_update; ++ u16 sae_token_idx; ++ u16 sae_pending_token_idx[256]; + int dot11RSNASAERetransPeriod; /* msec */ ++ struct dl_list sae_commit_queue; /* struct hostapd_sae_commit_queue */ + #endif /* CONFIG_SAE */ + + #ifdef CONFIG_TESTING_OPTIONS +@@ -277,7 +323,62 @@ + unsigned int ext_eapol_frame_io:1; + + struct l2_packet_data *l2_test; ++ ++ enum wpa_alg last_gtk_alg; ++ int last_gtk_key_idx; ++ u8 last_gtk[WPA_GTK_MAX_LEN]; ++ size_t last_gtk_len; ++ ++#ifdef CONFIG_IEEE80211W ++ enum wpa_alg last_igtk_alg; ++ int last_igtk_key_idx; ++ u8 last_igtk[WPA_IGTK_MAX_LEN]; ++ size_t last_igtk_len; ++#endif /* CONFIG_IEEE80211W */ + #endif /* CONFIG_TESTING_OPTIONS */ ++ ++#ifdef CONFIG_MBO ++ unsigned int mbo_assoc_disallow; ++#endif /* CONFIG_MBO */ ++ ++ struct dl_list nr_db; ++ ++ u8 beacon_req_token; ++ u8 lci_req_token; ++ u8 range_req_token; ++ unsigned int lci_req_active:1; ++ unsigned int range_req_active:1; ++ ++ int dhcp_sock; /* UDP socket used with the DHCP server */ ++ ++#ifdef CONFIG_DPP ++ int dpp_init_done; ++ struct dpp_authentication *dpp_auth; ++ u8 dpp_allowed_roles; ++ int dpp_qr_mutual; ++ int dpp_auth_ok_on_ack; ++ int dpp_in_response_listen; ++ struct gas_query_ap *gas; ++ struct dpp_pkex *dpp_pkex; ++ struct dpp_bootstrap_info *dpp_pkex_bi; ++ char *dpp_pkex_code; ++ char *dpp_pkex_identifier; ++ char *dpp_pkex_auth_cmd; ++ char *dpp_configurator_params; ++ struct os_reltime dpp_last_init; ++ struct os_reltime dpp_init_iter_start; ++ unsigned int dpp_init_max_tries; ++ unsigned int dpp_init_retry_time; ++ unsigned int dpp_resp_wait_time; ++ unsigned int dpp_resp_max_tries; ++ unsigned int dpp_resp_retry_time; ++#ifdef CONFIG_TESTING_OPTIONS ++ char *dpp_config_obj_override; ++ char *dpp_discovery_override; ++ char *dpp_groups_override; ++ unsigned int dpp_ignore_netaccesskey_mismatch:1; ++#endif /* CONFIG_TESTING_OPTIONS */ ++#endif /* CONFIG_DPP */ + }; + + +@@ -285,6 +386,10 @@ + struct dl_list list; + u8 addr[ETH_ALEN]; + struct os_reltime last_seen; ++ int ssi_signal; ++#ifdef CONFIG_TAXONOMY ++ struct wpabuf *probe_ie_taxonomy; ++#endif /* CONFIG_TAXONOMY */ + }; + + /** +@@ -327,6 +432,15 @@ + */ + unsigned int driver_ap_teardown:1; + ++ /* ++ * When set, indicates that this interface is part of list of ++ * interfaces that need to be started together (synchronously). ++ */ ++ unsigned int need_to_start_in_sync:1; ++ ++ /* Ready to start but waiting for other interfaces to become ready. */ ++ unsigned int ready_to_start_in_sync:1; ++ + int num_ap; /* number of entries in ap_list */ + struct ap_info *ap_list; /* AP info list head */ + struct ap_info *ap_hash[STA_HASH_SIZE]; +@@ -402,6 +516,13 @@ + u64 last_channel_time_busy; + u8 channel_utilization; + ++ unsigned int chan_util_samples_sum; ++ unsigned int chan_util_num_sample_periods; ++ unsigned int chan_util_average; ++ ++ /* eCSA IE will be added only if operating class is specified */ ++ u8 cs_oper_class; ++ + unsigned int dfs_cac_ms; + struct os_reltime dfs_cac_start; + +@@ -418,6 +539,8 @@ + + struct dl_list sta_seen; /* struct hostapd_sta_info */ + unsigned int num_sta_seen; ++ ++ u8 dfs_domain; + }; + + /* hostapd.c */ +@@ -425,6 +548,7 @@ + int (*cb)(struct hostapd_iface *iface, + void *ctx), void *ctx); + int hostapd_reload_config(struct hostapd_iface *iface); ++void hostapd_reconfig_encryption(struct hostapd_data *hapd); + struct hostapd_data * + hostapd_alloc_bss_data(struct hostapd_iface *hapd_iface, + struct hostapd_config *conf, +@@ -433,6 +557,7 @@ + int hostapd_setup_interface_complete(struct hostapd_iface *iface, int err); + void hostapd_interface_deinit(struct hostapd_iface *iface); + void hostapd_interface_free(struct hostapd_iface *iface); ++struct hostapd_iface * hostapd_alloc_iface(void); + struct hostapd_iface * hostapd_init(struct hapd_interfaces *interfaces, + const char *config_file); + struct hostapd_iface * +@@ -449,6 +574,8 @@ + void hostapd_channel_list_updated(struct hostapd_iface *iface, int initiator); + void hostapd_set_state(struct hostapd_iface *iface, enum hostapd_iface_state s); + const char * hostapd_state_text(enum hostapd_iface_state s); ++int hostapd_csa_in_progress(struct hostapd_iface *iface); ++void hostapd_chan_switch_vht_config(struct hostapd_data *hapd, int vht_enabled); + int hostapd_switch_channel(struct hostapd_data *hapd, + struct csa_settings *settings); + void +@@ -456,6 +583,7 @@ + const struct hostapd_freq_params *freq_params); + void hostapd_cleanup_cs_params(struct hostapd_data *hapd); + void hostapd_periodic_iface(struct hostapd_iface *iface); ++int hostapd_owe_trans_get_info(struct hostapd_data *hapd); + + /* utils.c */ + int hostapd_register_probereq_cb(struct hostapd_data *hapd, +@@ -467,6 +595,8 @@ + void hostapd_prune_associations(struct hostapd_data *hapd, const u8 *addr); + + /* drv_callbacks.c (TODO: move to somewhere else?) */ ++void hostapd_notify_assoc_fils_finish(struct hostapd_data *hapd, ++ struct sta_info *sta); + int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr, + const u8 *ie, size_t ielen, int reassoc); + void hostapd_notif_disassoc(struct hostapd_data *hapd, const u8 *addr); +@@ -478,6 +608,11 @@ + int ssi_signal); + void hostapd_event_ch_switch(struct hostapd_data *hapd, int freq, int ht, + int offset, int width, int cf1, int cf2); ++struct survey_results; ++void hostapd_event_get_survey(struct hostapd_iface *iface, ++ struct survey_results *survey_results); ++void hostapd_acs_channel_selected(struct hostapd_data *hapd, ++ struct acs_selected_channels *acs_res); + + const struct hostapd_eap_user * + hostapd_get_eap_user(struct hostapd_data *hapd, const u8 *identity, +@@ -485,6 +620,9 @@ + + struct hostapd_data * hostapd_get_iface(struct hapd_interfaces *interfaces, + const char *ifname); ++void hostapd_event_sta_opmode_changed(struct hostapd_data *hapd, const u8 *addr, ++ enum smps_mode smps_mode, ++ enum chan_width chan_width, u8 rx_nss); + + #ifdef CONFIG_FST + void fst_hostapd_fill_iface_obj(struct hostapd_data *hapd, +--- contrib/wpa/src/ap/hs20.c.orig ++++ contrib/wpa/src/ap/hs20.c +@@ -11,9 +11,11 @@ + + #include "common.h" + #include "common/ieee802_11_defs.h" ++#include "common/wpa_ctrl.h" + #include "hostapd.h" + #include "ap_config.h" + #include "ap_drv_ops.h" ++#include "sta_info.h" + #include "hs20.h" + + +@@ -23,17 +25,20 @@ + if (!hapd->conf->hs20) + return eid; + *eid++ = WLAN_EID_VENDOR_SPECIFIC; +- *eid++ = 7; ++ *eid++ = hapd->conf->hs20_release < 2 ? 5 : 7; + WPA_PUT_BE24(eid, OUI_WFA); + eid += 3; + *eid++ = HS20_INDICATION_OUI_TYPE; +- conf = HS20_VERSION; /* Release Number */ +- conf |= HS20_ANQP_DOMAIN_ID_PRESENT; ++ conf = (hapd->conf->hs20_release - 1) << 4; /* Release Number */ ++ if (hapd->conf->hs20_release >= 2) ++ conf |= HS20_ANQP_DOMAIN_ID_PRESENT; + if (hapd->conf->disable_dgaf) + conf |= HS20_DGAF_DISABLED; + *eid++ = conf; +- WPA_PUT_LE16(eid, hapd->conf->anqp_domain_id); +- eid += 2; ++ if (hapd->conf->hs20_release >= 2) { ++ WPA_PUT_LE16(eid, hapd->conf->anqp_domain_id); ++ eid += 2; ++ } + + return eid; + } +@@ -82,6 +87,10 @@ + capab |= WPA_CAPABILITY_MFPR; + } + #endif /* CONFIG_IEEE80211W */ ++#ifdef CONFIG_OCV ++ if (hapd->conf->ocv) ++ capab |= WPA_CAPABILITY_OCVC; ++#endif /* CONFIG_OCV */ + WPA_PUT_LE16(eid, capab); + eid += 2; + +@@ -175,3 +184,72 @@ + + return ret; + } ++ ++ ++int hs20_send_wnm_notification_t_c(struct hostapd_data *hapd, ++ const u8 *addr, const char *url) ++{ ++ struct wpabuf *buf; ++ int ret; ++ size_t url_len; ++ ++ if (!url) { ++ wpa_printf(MSG_INFO, "HS 2.0: No T&C Server URL available"); ++ return -1; ++ } ++ ++ url_len = os_strlen(url); ++ if (5 + url_len > 255) { ++ wpa_printf(MSG_INFO, ++ "HS 2.0: Too long T&C Server URL for WNM-Notification: '%s'", ++ url); ++ return -1; ++ } ++ ++ buf = wpabuf_alloc(4 + 7 + url_len); ++ if (!buf) ++ return -1; ++ ++ wpabuf_put_u8(buf, WLAN_ACTION_WNM); ++ wpabuf_put_u8(buf, WNM_NOTIFICATION_REQ); ++ wpabuf_put_u8(buf, 1); /* Dialog token */ ++ wpabuf_put_u8(buf, 1); /* Type - 1 reserved for WFA */ ++ ++ /* Terms and Conditions Acceptance subelement */ ++ wpabuf_put_u8(buf, WLAN_EID_VENDOR_SPECIFIC); ++ wpabuf_put_u8(buf, 4 + 1 + url_len); ++ wpabuf_put_be24(buf, OUI_WFA); ++ wpabuf_put_u8(buf, HS20_WNM_T_C_ACCEPTANCE); ++ wpabuf_put_u8(buf, url_len); ++ wpabuf_put_str(buf, url); ++ ++ ret = hostapd_drv_send_action(hapd, hapd->iface->freq, 0, addr, ++ wpabuf_head(buf), wpabuf_len(buf)); ++ ++ wpabuf_free(buf); ++ ++ return ret; ++} ++ ++ ++void hs20_t_c_filtering(struct hostapd_data *hapd, struct sta_info *sta, ++ int enabled) ++{ ++ if (enabled) { ++ wpa_printf(MSG_DEBUG, ++ "HS 2.0: Terms and Conditions filtering required for " ++ MACSTR, MAC2STR(sta->addr)); ++ sta->hs20_t_c_filtering = 1; ++ /* TODO: Enable firewall filtering for the STA */ ++ wpa_msg(hapd->msg_ctx, MSG_INFO, HS20_T_C_FILTERING_ADD MACSTR, ++ MAC2STR(sta->addr)); ++ } else { ++ wpa_printf(MSG_DEBUG, ++ "HS 2.0: Terms and Conditions filtering not required for " ++ MACSTR, MAC2STR(sta->addr)); ++ sta->hs20_t_c_filtering = 0; ++ /* TODO: Disable firewall filtering for the STA */ ++ wpa_msg(hapd->msg_ctx, MSG_INFO, ++ HS20_T_C_FILTERING_REMOVE MACSTR, MAC2STR(sta->addr)); ++ } ++} +--- contrib/wpa/src/ap/hs20.h.orig ++++ contrib/wpa/src/ap/hs20.h +@@ -18,5 +18,9 @@ + int hs20_send_wnm_notification_deauth_req(struct hostapd_data *hapd, + const u8 *addr, + const struct wpabuf *payload); ++int hs20_send_wnm_notification_t_c(struct hostapd_data *hapd, ++ const u8 *addr, const char *url); ++void hs20_t_c_filtering(struct hostapd_data *hapd, struct sta_info *sta, ++ int enabled); + + #endif /* HS20_H */ +--- contrib/wpa/src/ap/hw_features.c.orig ++++ contrib/wpa/src/ap/hw_features.c +@@ -78,10 +78,12 @@ + int i, j; + u16 num_modes, flags; + struct hostapd_hw_modes *modes; ++ u8 dfs_domain; + + if (hostapd_drv_none(hapd)) + return -1; +- modes = hostapd_get_hw_feature_data(hapd, &num_modes, &flags); ++ modes = hostapd_get_hw_feature_data(hapd, &num_modes, &flags, ++ &dfs_domain); + if (modes == NULL) { + hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_DEBUG, +@@ -91,6 +93,7 @@ + } + + iface->hw_flags = flags; ++ iface->dfs_domain = dfs_domain; + + hostapd_free_hw_features(iface->hw_features, iface->num_hw_features); + iface->hw_features = modes; +@@ -226,9 +229,6 @@ + { + int pri_chan, sec_chan; + +- if (!iface->conf->secondary_channel) +- return 1; /* HT40 not used */ +- + pri_chan = iface->conf->channel; + sec_chan = pri_chan + iface->conf->secondary_channel * 4; + +@@ -329,6 +329,10 @@ + res = ieee80211n_allowed_ht40_channel_pair(iface); + if (!res) { + iface->conf->secondary_channel = 0; ++ iface->conf->vht_oper_centr_freq_seg0_idx = 0; ++ iface->conf->vht_oper_centr_freq_seg1_idx = 0; ++ iface->conf->vht_oper_chwidth = VHT_CHANWIDTH_USE_HT; ++ res = 1; + wpa_printf(MSG_INFO, "Fallback to 20 MHz"); + } + +@@ -472,8 +476,9 @@ + struct wpa_driver_scan_params params; + int ret; + +- if (!iface->conf->secondary_channel) +- return 0; /* HT40 not used */ ++ /* Check that HT40 is used and PRI / SEC switch is allowed */ ++ if (!iface->conf->secondary_channel || iface->conf->no_pri_sec_switch) ++ return 0; + + hostapd_set_state(iface, HAPD_IFACE_HT_SCAN); + wpa_printf(MSG_DEBUG, "Scan for neighboring BSSes prior to enabling " +@@ -619,41 +624,6 @@ + + + #ifdef CONFIG_IEEE80211AC +- +-static int ieee80211ac_cap_check(u32 hw, u32 conf, u32 cap, const char *name) +-{ +- u32 req_cap = conf & cap; +- +- /* +- * Make sure we support all requested capabilities. +- * NOTE: We assume that 'cap' represents a capability mask, +- * not a discrete value. +- */ +- if ((hw & req_cap) != req_cap) { +- wpa_printf(MSG_ERROR, "Driver does not support configured VHT capability [%s]", +- name); +- return 0; +- } +- return 1; +-} +- +- +-static int ieee80211ac_cap_check_max(u32 hw, u32 conf, u32 mask, +- unsigned int shift, +- const char *name) +-{ +- u32 hw_max = hw & mask; +- u32 conf_val = conf & mask; +- +- if (conf_val > hw_max) { +- wpa_printf(MSG_ERROR, "Configured VHT capability [%s] exceeds max value supported by the driver (%d > %d)", +- name, conf_val >> shift, hw_max >> shift); +- return 0; +- } +- return 1; +-} +- +- + static int ieee80211ac_supported_vht_capab(struct hostapd_iface *iface) + { + struct hostapd_hw_modes *mode = iface->current_mode; +@@ -681,45 +651,7 @@ + } + } + +-#define VHT_CAP_CHECK(cap) \ +- do { \ +- if (!ieee80211ac_cap_check(hw, conf, cap, #cap)) \ +- return 0; \ +- } while (0) +- +-#define VHT_CAP_CHECK_MAX(cap) \ +- do { \ +- if (!ieee80211ac_cap_check_max(hw, conf, cap, cap ## _SHIFT, \ +- #cap)) \ +- return 0; \ +- } while (0) +- +- VHT_CAP_CHECK_MAX(VHT_CAP_MAX_MPDU_LENGTH_MASK); +- VHT_CAP_CHECK(VHT_CAP_SUPP_CHAN_WIDTH_160MHZ); +- VHT_CAP_CHECK(VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ); +- VHT_CAP_CHECK(VHT_CAP_RXLDPC); +- VHT_CAP_CHECK(VHT_CAP_SHORT_GI_80); +- VHT_CAP_CHECK(VHT_CAP_SHORT_GI_160); +- VHT_CAP_CHECK(VHT_CAP_TXSTBC); +- VHT_CAP_CHECK_MAX(VHT_CAP_RXSTBC_MASK); +- VHT_CAP_CHECK(VHT_CAP_SU_BEAMFORMER_CAPABLE); +- VHT_CAP_CHECK(VHT_CAP_SU_BEAMFORMEE_CAPABLE); +- VHT_CAP_CHECK_MAX(VHT_CAP_BEAMFORMEE_STS_MAX); +- VHT_CAP_CHECK_MAX(VHT_CAP_SOUNDING_DIMENSION_MAX); +- VHT_CAP_CHECK(VHT_CAP_MU_BEAMFORMER_CAPABLE); +- VHT_CAP_CHECK(VHT_CAP_MU_BEAMFORMEE_CAPABLE); +- VHT_CAP_CHECK(VHT_CAP_VHT_TXOP_PS); +- VHT_CAP_CHECK(VHT_CAP_HTC_VHT); +- VHT_CAP_CHECK_MAX(VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MAX); +- VHT_CAP_CHECK(VHT_CAP_VHT_LINK_ADAPTATION_VHT_UNSOL_MFB); +- VHT_CAP_CHECK(VHT_CAP_VHT_LINK_ADAPTATION_VHT_MRQ_MFB); +- VHT_CAP_CHECK(VHT_CAP_RX_ANTENNA_PATTERN); +- VHT_CAP_CHECK(VHT_CAP_TX_ANTENNA_PATTERN); +- +-#undef VHT_CAP_CHECK +-#undef VHT_CAP_CHECK_MAX +- +- return 1; ++ return ieee80211ac_cap_check(hw, conf); + } + #endif /* CONFIG_IEEE80211AC */ + +@@ -744,7 +676,8 @@ + if (!ieee80211n_supported_ht_capab(iface)) + return -1; + #ifdef CONFIG_IEEE80211AC +- if (!ieee80211ac_supported_vht_capab(iface)) ++ if (iface->conf->ieee80211ac && ++ !ieee80211ac_supported_vht_capab(iface)) + return -1; + #endif /* CONFIG_IEEE80211AC */ + ret = ieee80211n_check_40mhz(iface); +@@ -761,28 +694,25 @@ + static int hostapd_is_usable_chan(struct hostapd_iface *iface, + int channel, int primary) + { +- int i; + struct hostapd_channel_data *chan; + + if (!iface->current_mode) + return 0; + +- for (i = 0; i < iface->current_mode->num_channels; i++) { +- chan = &iface->current_mode->channels[i]; +- if (chan->chan != channel) +- continue; ++ chan = hw_get_channel_chan(iface->current_mode, channel, NULL); ++ if (!chan) ++ return 0; + +- if (!(chan->flag & HOSTAPD_CHAN_DISABLED)) +- return 1; ++ if ((primary && chan_pri_allowed(chan)) || ++ (!primary && !(chan->flag & HOSTAPD_CHAN_DISABLED))) ++ return 1; + +- wpa_printf(MSG_DEBUG, +- "%schannel [%i] (%i) is disabled for use in AP mode, flags: 0x%x%s%s", +- primary ? "" : "Configured HT40 secondary ", +- i, chan->chan, chan->flag, +- chan->flag & HOSTAPD_CHAN_NO_IR ? " NO-IR" : "", +- chan->flag & HOSTAPD_CHAN_RADAR ? " RADAR" : ""); +- } +- ++ wpa_printf(MSG_INFO, ++ "Channel %d (%s) not allowed for AP mode, flags: 0x%x%s%s", ++ channel, primary ? "primary" : "secondary", ++ chan->flag, ++ chan->flag & HOSTAPD_CHAN_NO_IR ? " NO-IR" : "", ++ chan->flag & HOSTAPD_CHAN_RADAR ? " RADAR" : ""); + return 0; + } + +@@ -789,6 +719,14 @@ + + static int hostapd_is_usable_chans(struct hostapd_iface *iface) + { ++ int secondary_chan; ++ struct hostapd_channel_data *pri_chan; ++ ++ pri_chan = hw_get_channel_chan(iface->current_mode, ++ iface->conf->channel, NULL); ++ if (!pri_chan) ++ return 0; ++ + if (!hostapd_is_usable_chan(iface, iface->conf->channel, 1)) + return 0; + +@@ -795,8 +733,27 @@ + if (!iface->conf->secondary_channel) + return 1; + +- return hostapd_is_usable_chan(iface, iface->conf->channel + +- iface->conf->secondary_channel * 4, 0); ++ if (!iface->conf->ht40_plus_minus_allowed) ++ return hostapd_is_usable_chan( ++ iface, iface->conf->channel + ++ iface->conf->secondary_channel * 4, 0); ++ ++ /* Both HT40+ and HT40- are set, pick a valid secondary channel */ ++ secondary_chan = iface->conf->channel + 4; ++ if (hostapd_is_usable_chan(iface, secondary_chan, 0) && ++ (pri_chan->allowed_bw & HOSTAPD_CHAN_WIDTH_40P)) { ++ iface->conf->secondary_channel = 1; ++ return 1; ++ } ++ ++ secondary_chan = iface->conf->channel - 4; ++ if (hostapd_is_usable_chan(iface, secondary_chan, 0) && ++ (pri_chan->allowed_bw & HOSTAPD_CHAN_WIDTH_40M)) { ++ iface->conf->secondary_channel = -1; ++ return 1; ++ } ++ ++ return 0; + } + + +@@ -976,5 +933,19 @@ + + int hostapd_hw_get_channel(struct hostapd_data *hapd, int freq) + { +- return hw_get_chan(hapd->iface->current_mode, freq); ++ int i, channel; ++ struct hostapd_hw_modes *mode; ++ ++ channel = hw_get_chan(hapd->iface->current_mode, freq); ++ if (channel) ++ return channel; ++ /* Check other available modes since the channel list for the current ++ * mode did not include the specified frequency. */ ++ for (i = 0; i < hapd->iface->num_hw_features; i++) { ++ mode = &hapd->iface->hw_features[i]; ++ channel = hw_get_chan(mode, freq); ++ if (channel) ++ return channel; ++ } ++ return 0; + } +--- contrib/wpa/src/ap/iapp.c.orig ++++ contrib/wpa/src/ap/iapp.c +@@ -34,11 +34,7 @@ + #include "utils/includes.h" + #include + #include +-#ifdef USE_KERNEL_HEADERS +-#include +-#else /* USE_KERNEL_HEADERS */ + #include +-#endif /* USE_KERNEL_HEADERS */ + + #include "utils/common.h" + #include "utils/eloop.h" +@@ -385,6 +381,7 @@ + struct sockaddr_in *paddr, uaddr; + struct iapp_data *iapp; + struct ip_mreqn mreq; ++ int reuseaddr = 1; + + iapp = os_zalloc(sizeof(*iapp)); + if (iapp == NULL) +@@ -447,6 +444,18 @@ + os_memset(&uaddr, 0, sizeof(uaddr)); + uaddr.sin_family = AF_INET; + uaddr.sin_port = htons(IAPP_UDP_PORT); ++ ++ if (setsockopt(iapp->udp_sock, SOL_SOCKET, SO_REUSEADDR, &reuseaddr, ++ sizeof(reuseaddr)) < 0) { ++ wpa_printf(MSG_INFO, ++ "iapp_init - setsockopt[UDP,SO_REUSEADDR]: %s", ++ strerror(errno)); ++ /* ++ * Ignore this and try to continue. This is fine for single ++ * BSS cases, but may fail if multiple BSSes enable IAPP. ++ */ ++ } ++ + if (bind(iapp->udp_sock, (struct sockaddr *) &uaddr, + sizeof(uaddr)) < 0) { + wpa_printf(MSG_INFO, "iapp_init - bind[UDP]: %s", +--- contrib/wpa/src/ap/ieee802_11.c.orig ++++ contrib/wpa/src/ap/ieee802_11.c +@@ -1,6 +1,6 @@ + /* + * hostapd / IEEE 802.11 Management +- * Copyright (c) 2002-2014, Jouni Malinen ++ * Copyright (c) 2002-2017, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. +@@ -14,11 +14,15 @@ + #include "utils/eloop.h" + #include "crypto/crypto.h" + #include "crypto/sha256.h" ++#include "crypto/sha384.h" ++#include "crypto/sha512.h" + #include "crypto/random.h" + #include "common/ieee802_11_defs.h" + #include "common/ieee802_11_common.h" + #include "common/wpa_ctrl.h" + #include "common/sae.h" ++#include "common/dpp.h" ++#include "common/ocv.h" + #include "radius/radius.h" + #include "radius/radius_client.h" + #include "p2p/p2p.h" +@@ -42,8 +46,43 @@ + #include "hw_features.h" + #include "ieee802_11.h" + #include "dfs.h" ++#include "mbo_ap.h" ++#include "rrm.h" ++#include "taxonomy.h" ++#include "fils_hlp.h" ++#include "dpp_hostapd.h" ++#include "gas_query_ap.h" + + ++#ifdef CONFIG_FILS ++static struct wpabuf * ++prepare_auth_resp_fils(struct hostapd_data *hapd, ++ struct sta_info *sta, u16 *resp, ++ struct rsn_pmksa_cache_entry *pmksa, ++ struct wpabuf *erp_resp, ++ const u8 *msk, size_t msk_len, ++ int *is_pub); ++#endif /* CONFIG_FILS */ ++static void handle_auth(struct hostapd_data *hapd, ++ const struct ieee80211_mgmt *mgmt, size_t len, ++ int rssi, int from_queue); ++ ++ ++u8 * hostapd_eid_multi_ap(struct hostapd_data *hapd, u8 *eid) ++{ ++ u8 multi_ap_val = 0; ++ ++ if (!hapd->conf->multi_ap) ++ return eid; ++ if (hapd->conf->multi_ap & BACKHAUL_BSS) ++ multi_ap_val |= MULTI_AP_BACKHAUL_BSS; ++ if (hapd->conf->multi_ap & FRONTHAUL_BSS) ++ multi_ap_val |= MULTI_AP_FRONTHAUL_BSS; ++ ++ return eid + add_multi_ap_ie(eid, 9, multi_ap_val); ++} ++ ++ + u8 * hostapd_eid_supp_rates(struct hostapd_data *hapd, u8 *eid) + { + u8 *pos = eid; +@@ -139,6 +178,7 @@ + int capab = WLAN_CAPABILITY_ESS; + int privacy; + int dfs; ++ int i; + + /* Check if any of configured channels require DFS */ + dfs = hostapd_is_dfs_required(hapd->iface); +@@ -186,8 +226,12 @@ + (hapd->iconf->spectrum_mgmt_required || dfs)) + capab |= WLAN_CAPABILITY_SPECTRUM_MGMT; + +- if (hapd->conf->radio_measurements) +- capab |= IEEE80211_CAP_RRM; ++ for (i = 0; i < RRM_CAPABILITIES_IE_LEN; i++) { ++ if (hapd->conf->radio_measurements[i]) { ++ capab |= IEEE80211_CAP_RRM; ++ break; ++ } ++ } + + return capab; + } +@@ -207,16 +251,17 @@ + if (!sta->challenge) { + /* Generate a pseudo-random challenge */ + u8 key[8]; +- struct os_time now; +- int r; ++ + sta->challenge = os_zalloc(WLAN_AUTH_CHALLENGE_LEN); + if (sta->challenge == NULL) + return WLAN_STATUS_UNSPECIFIED_FAILURE; + +- os_get_time(&now); +- r = os_random(); +- os_memcpy(key, &now.sec, 4); +- os_memcpy(key + 4, &r, 4); ++ if (os_get_random(key, sizeof(key)) < 0) { ++ os_free(sta->challenge); ++ sta->challenge = NULL; ++ return WLAN_STATUS_UNSPECIFIED_FAILURE; ++ } ++ + rc4_skip(key, sizeof(key), 0, + sta->challenge, WLAN_AUTH_CHALLENGE_LEN); + } +@@ -250,19 +295,20 @@ + #endif /* CONFIG_NO_RC4 */ + + +-static void send_auth_reply(struct hostapd_data *hapd, +- const u8 *dst, const u8 *bssid, +- u16 auth_alg, u16 auth_transaction, u16 resp, +- const u8 *ies, size_t ies_len) ++static int send_auth_reply(struct hostapd_data *hapd, ++ const u8 *dst, const u8 *bssid, ++ u16 auth_alg, u16 auth_transaction, u16 resp, ++ const u8 *ies, size_t ies_len, const char *dbg) + { + struct ieee80211_mgmt *reply; + u8 *buf; + size_t rlen; ++ int reply_res = WLAN_STATUS_UNSPECIFIED_FAILURE; + + rlen = IEEE80211_HDRLEN + sizeof(reply->u.auth) + ies_len; + buf = os_zalloc(rlen); + if (buf == NULL) +- return; ++ return -1; + + reply = (struct ieee80211_mgmt *) buf; + reply->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, +@@ -279,17 +325,21 @@ + os_memcpy(reply->u.auth.variable, ies, ies_len); + + wpa_printf(MSG_DEBUG, "authentication reply: STA=" MACSTR +- " auth_alg=%d auth_transaction=%d resp=%d (IE len=%lu)", ++ " auth_alg=%d auth_transaction=%d resp=%d (IE len=%lu) (dbg=%s)", + MAC2STR(dst), auth_alg, auth_transaction, +- resp, (unsigned long) ies_len); ++ resp, (unsigned long) ies_len, dbg); + if (hostapd_drv_send_mlme(hapd, reply, rlen, 0) < 0) +- wpa_printf(MSG_INFO, "send_auth_reply: send"); ++ wpa_printf(MSG_INFO, "send_auth_reply: send failed"); ++ else ++ reply_res = WLAN_STATUS_SUCCESS; + + os_free(buf); ++ ++ return reply_res; + } + + +-#ifdef CONFIG_IEEE80211R ++#ifdef CONFIG_IEEE80211R_AP + static void handle_auth_ft_finish(void *ctx, const u8 *dst, const u8 *bssid, + u16 auth_transaction, u16 status, + const u8 *ies, size_t ies_len) +@@ -296,28 +346,44 @@ + { + struct hostapd_data *hapd = ctx; + struct sta_info *sta; ++ int reply_res; + +- send_auth_reply(hapd, dst, bssid, WLAN_AUTH_FT, auth_transaction, +- status, ies, ies_len); ++ reply_res = send_auth_reply(hapd, dst, bssid, WLAN_AUTH_FT, ++ auth_transaction, status, ies, ies_len, ++ "auth-ft-finish"); + +- if (status != WLAN_STATUS_SUCCESS) +- return; +- + sta = ap_get_sta(hapd, dst); + if (sta == NULL) + return; + ++ if (sta->added_unassoc && (reply_res != WLAN_STATUS_SUCCESS || ++ status != WLAN_STATUS_SUCCESS)) { ++ hostapd_drv_sta_remove(hapd, sta->addr); ++ sta->added_unassoc = 0; ++ return; ++ } ++ ++ if (status != WLAN_STATUS_SUCCESS) ++ return; ++ + hostapd_logger(hapd, dst, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_DEBUG, "authentication OK (FT)"); + sta->flags |= WLAN_STA_AUTH; + mlme_authenticate_indication(hapd, sta); + } +-#endif /* CONFIG_IEEE80211R */ ++#endif /* CONFIG_IEEE80211R_AP */ + + + #ifdef CONFIG_SAE + +-#define dot11RSNASAESync 5 /* attempts */ ++static void sae_set_state(struct sta_info *sta, enum sae_state state, ++ const char *reason) ++{ ++ wpa_printf(MSG_DEBUG, "SAE: State %s -> %s for peer " MACSTR " (%s)", ++ sae_state_txt(sta->sae->state), sae_state_txt(state), ++ MAC2STR(sta->addr), reason); ++ sta->sae->state = state; ++} + + + static struct wpabuf * auth_build_sae_commit(struct hostapd_data *hapd, +@@ -324,8 +390,28 @@ + struct sta_info *sta, int update) + { + struct wpabuf *buf; ++ const char *password = NULL; ++ struct sae_password_entry *pw; ++ const char *rx_id = NULL; + +- if (hapd->conf->ssid.wpa_passphrase == NULL) { ++ if (sta->sae->tmp) ++ rx_id = sta->sae->tmp->pw_id; ++ ++ for (pw = hapd->conf->sae_passwords; pw; pw = pw->next) { ++ if (!is_broadcast_ether_addr(pw->peer_addr) && ++ os_memcmp(pw->peer_addr, sta->addr, ETH_ALEN) != 0) ++ continue; ++ if ((rx_id && !pw->identifier) || (!rx_id && pw->identifier)) ++ continue; ++ if (rx_id && pw->identifier && ++ os_strcmp(rx_id, pw->identifier) != 0) ++ continue; ++ password = pw->password; ++ break; ++ } ++ if (!password) ++ password = hapd->conf->ssid.wpa_passphrase; ++ if (!password) { + wpa_printf(MSG_DEBUG, "SAE: No password available"); + return NULL; + } +@@ -332,18 +418,27 @@ + + if (update && + sae_prepare_commit(hapd->own_addr, sta->addr, +- (u8 *) hapd->conf->ssid.wpa_passphrase, +- os_strlen(hapd->conf->ssid.wpa_passphrase), ++ (u8 *) password, os_strlen(password), rx_id, + sta->sae) < 0) { + wpa_printf(MSG_DEBUG, "SAE: Could not pick PWE"); + return NULL; + } + +- buf = wpabuf_alloc(SAE_COMMIT_MAX_LEN); ++ if (pw && pw->vlan_id) { ++ if (!sta->sae->tmp) { ++ wpa_printf(MSG_INFO, ++ "SAE: No temporary data allocated - cannot store VLAN ID"); ++ return NULL; ++ } ++ sta->sae->tmp->vlan_id = pw->vlan_id; ++ } ++ ++ buf = wpabuf_alloc(SAE_COMMIT_MAX_LEN + ++ (rx_id ? 3 + os_strlen(rx_id) : 0)); + if (buf == NULL) + return NULL; + sae_write_commit(sta->sae, buf, sta->sae->tmp ? +- sta->sae->tmp->anti_clogging_token : NULL); ++ sta->sae->tmp->anti_clogging_token : NULL, rx_id); + + return buf; + } +@@ -369,18 +464,21 @@ + const u8 *bssid, int update) + { + struct wpabuf *data; ++ int reply_res; + + data = auth_build_sae_commit(hapd, sta, update); ++ if (!data && sta->sae->tmp && sta->sae->tmp->pw_id) ++ return WLAN_STATUS_UNKNOWN_PASSWORD_IDENTIFIER; + if (data == NULL) + return WLAN_STATUS_UNSPECIFIED_FAILURE; + +- send_auth_reply(hapd, sta->addr, bssid, +- WLAN_AUTH_SAE, 1, WLAN_STATUS_SUCCESS, +- wpabuf_head(data), wpabuf_len(data)); ++ reply_res = send_auth_reply(hapd, sta->addr, bssid, WLAN_AUTH_SAE, 1, ++ WLAN_STATUS_SUCCESS, wpabuf_head(data), ++ wpabuf_len(data), "sae-send-commit"); + + wpabuf_free(data); + +- return WLAN_STATUS_SUCCESS; ++ return reply_res; + } + + +@@ -389,18 +487,19 @@ + const u8 *bssid) + { + struct wpabuf *data; ++ int reply_res; + + data = auth_build_sae_confirm(hapd, sta); + if (data == NULL) + return WLAN_STATUS_UNSPECIFIED_FAILURE; + +- send_auth_reply(hapd, sta->addr, bssid, +- WLAN_AUTH_SAE, 2, WLAN_STATUS_SUCCESS, +- wpabuf_head(data), wpabuf_len(data)); ++ reply_res = send_auth_reply(hapd, sta->addr, bssid, WLAN_AUTH_SAE, 2, ++ WLAN_STATUS_SUCCESS, wpabuf_head(data), ++ wpabuf_len(data), "sae-send-confirm"); + + wpabuf_free(data); + +- return WLAN_STATUS_SUCCESS; ++ return reply_res; + } + + +@@ -423,22 +522,58 @@ + return 1; + } + ++ /* In addition to already existing open SAE sessions, check whether ++ * there are enough pending commit messages in the processing queue to ++ * potentially result in too many open sessions. */ ++ if (open + dl_list_len(&hapd->sae_commit_queue) >= ++ hapd->conf->sae_anti_clogging_threshold) ++ return 1; ++ + return 0; + } + + ++static u8 sae_token_hash(struct hostapd_data *hapd, const u8 *addr) ++{ ++ u8 hash[SHA256_MAC_LEN]; ++ ++ hmac_sha256(hapd->sae_token_key, sizeof(hapd->sae_token_key), ++ addr, ETH_ALEN, hash); ++ return hash[0]; ++} ++ ++ + static int check_sae_token(struct hostapd_data *hapd, const u8 *addr, + const u8 *token, size_t token_len) + { + u8 mac[SHA256_MAC_LEN]; ++ const u8 *addrs[2]; ++ size_t len[2]; ++ u16 token_idx; ++ u8 idx; + + if (token_len != SHA256_MAC_LEN) + return -1; +- if (hmac_sha256(hapd->sae_token_key, sizeof(hapd->sae_token_key), +- addr, ETH_ALEN, mac) < 0 || +- os_memcmp_const(token, mac, SHA256_MAC_LEN) != 0) ++ idx = sae_token_hash(hapd, addr); ++ token_idx = hapd->sae_pending_token_idx[idx]; ++ if (token_idx == 0 || token_idx != WPA_GET_BE16(token)) { ++ wpa_printf(MSG_DEBUG, "SAE: Invalid anti-clogging token from " ++ MACSTR " - token_idx 0x%04x, expected 0x%04x", ++ MAC2STR(addr), WPA_GET_BE16(token), token_idx); + return -1; ++ } + ++ addrs[0] = addr; ++ len[0] = ETH_ALEN; ++ addrs[1] = token; ++ len[1] = 2; ++ if (hmac_sha256_vector(hapd->sae_token_key, sizeof(hapd->sae_token_key), ++ 2, addrs, len, mac) < 0 || ++ os_memcmp_const(token + 2, &mac[2], SHA256_MAC_LEN - 2) != 0) ++ return -1; ++ ++ hapd->sae_pending_token_idx[idx] = 0; /* invalidate used token */ ++ + return 0; + } + +@@ -449,10 +584,16 @@ + struct wpabuf *buf; + u8 *token; + struct os_reltime now; ++ u8 idx[2]; ++ const u8 *addrs[2]; ++ size_t len[2]; ++ u8 p_idx; ++ u16 token_idx; + + os_get_reltime(&now); + if (!os_reltime_initialized(&hapd->last_sae_token_key_update) || +- os_reltime_expired(&now, &hapd->last_sae_token_key_update, 60)) { ++ os_reltime_expired(&now, &hapd->last_sae_token_key_update, 60) || ++ hapd->sae_token_idx == 0xffff) { + if (random_get_bytes(hapd->sae_token_key, + sizeof(hapd->sae_token_key)) < 0) + return NULL; +@@ -459,6 +600,9 @@ + wpa_hexdump(MSG_DEBUG, "SAE: Updated token key", + hapd->sae_token_key, sizeof(hapd->sae_token_key)); + hapd->last_sae_token_key_update = now; ++ hapd->sae_token_idx = 0; ++ os_memset(hapd->sae_pending_token_idx, 0, ++ sizeof(hapd->sae_pending_token_idx)); + } + + buf = wpabuf_alloc(sizeof(le16) + SHA256_MAC_LEN); +@@ -467,18 +611,34 @@ + + wpabuf_put_le16(buf, group); /* Finite Cyclic Group */ + ++ p_idx = sae_token_hash(hapd, addr); ++ token_idx = hapd->sae_pending_token_idx[p_idx]; ++ if (!token_idx) { ++ hapd->sae_token_idx++; ++ token_idx = hapd->sae_token_idx; ++ hapd->sae_pending_token_idx[p_idx] = token_idx; ++ } ++ WPA_PUT_BE16(idx, token_idx); + token = wpabuf_put(buf, SHA256_MAC_LEN); +- hmac_sha256(hapd->sae_token_key, sizeof(hapd->sae_token_key), +- addr, ETH_ALEN, token); ++ addrs[0] = addr; ++ len[0] = ETH_ALEN; ++ addrs[1] = idx; ++ len[1] = sizeof(idx); ++ if (hmac_sha256_vector(hapd->sae_token_key, sizeof(hapd->sae_token_key), ++ 2, addrs, len, token) < 0) { ++ wpabuf_free(buf); ++ return NULL; ++ } ++ WPA_PUT_BE16(token, token_idx); + + return buf; + } + + +-static int sae_check_big_sync(struct sta_info *sta) ++static int sae_check_big_sync(struct hostapd_data *hapd, struct sta_info *sta) + { +- if (sta->sae->sync > dot11RSNASAESync) { +- sta->sae->state = SAE_NOTHING; ++ if (sta->sae->sync > hapd->conf->sae_sync) { ++ sae_set_state(sta, SAE_NOTHING, "Sync > dot11RSNASAESync"); + sta->sae->sync = 0; + return -1; + } +@@ -492,9 +652,13 @@ + struct sta_info *sta = eloop_data; + int ret; + +- if (sae_check_big_sync(sta)) ++ if (sae_check_big_sync(hapd, sta)) + return; + sta->sae->sync++; ++ wpa_printf(MSG_DEBUG, "SAE: Auth SAE retransmit timer for " MACSTR ++ " (sync=%d state=%s)", ++ MAC2STR(sta->addr), sta->sae->sync, ++ sae_state_txt(sta->sae->state)); + + switch (sta->sae->state) { + case SAE_COMMITTED: +@@ -537,21 +701,85 @@ + } + + ++static void sae_sme_send_external_auth_status(struct hostapd_data *hapd, ++ struct sta_info *sta, u16 status) ++{ ++ struct external_auth params; ++ ++ os_memset(¶ms, 0, sizeof(params)); ++ params.status = status; ++ params.bssid = sta->addr; ++ if (status == WLAN_STATUS_SUCCESS && sta->sae) ++ params.pmkid = sta->sae->pmkid; ++ ++ hostapd_drv_send_external_auth_status(hapd, ¶ms); ++} ++ ++ ++void sae_accept_sta(struct hostapd_data *hapd, struct sta_info *sta) ++{ ++#ifndef CONFIG_NO_VLAN ++ struct vlan_description vlan_desc; ++ ++ if (sta->sae->tmp && sta->sae->tmp->vlan_id > 0) { ++ wpa_printf(MSG_DEBUG, "SAE: Assign STA " MACSTR ++ " to VLAN ID %d", ++ MAC2STR(sta->addr), sta->sae->tmp->vlan_id); ++ ++ os_memset(&vlan_desc, 0, sizeof(vlan_desc)); ++ vlan_desc.notempty = 1; ++ vlan_desc.untagged = sta->sae->tmp->vlan_id; ++ if (!hostapd_vlan_valid(hapd->conf->vlan, &vlan_desc)) { ++ wpa_printf(MSG_INFO, ++ "Invalid VLAN ID %d in sae_password", ++ sta->sae->tmp->vlan_id); ++ return; ++ } ++ ++ if (ap_sta_set_vlan(hapd, sta, &vlan_desc) < 0 || ++ ap_sta_bind_vlan(hapd, sta) < 0) { ++ wpa_printf(MSG_INFO, ++ "Failed to assign VLAN ID %d from sae_password to " ++ MACSTR, sta->sae->tmp->vlan_id, ++ MAC2STR(sta->addr)); ++ return; ++ } ++ } ++#endif /* CONFIG_NO_VLAN */ ++ ++ sta->flags |= WLAN_STA_AUTH; ++ sta->auth_alg = WLAN_AUTH_SAE; ++ mlme_authenticate_indication(hapd, sta); ++ wpa_auth_sm_event(sta->wpa_sm, WPA_AUTH); ++ sae_set_state(sta, SAE_ACCEPTED, "Accept Confirm"); ++ wpa_auth_pmksa_add_sae(hapd->wpa_auth, sta->addr, ++ sta->sae->pmk, sta->sae->pmkid); ++ sae_sme_send_external_auth_status(hapd, sta, WLAN_STATUS_SUCCESS); ++} ++ ++ + static int sae_sm_step(struct hostapd_data *hapd, struct sta_info *sta, +- const u8 *bssid, u8 auth_transaction) ++ const u8 *bssid, u8 auth_transaction, int allow_reuse, ++ int *sta_removed) + { + int ret; + ++ *sta_removed = 0; ++ + if (auth_transaction != 1 && auth_transaction != 2) + return WLAN_STATUS_UNSPECIFIED_FAILURE; + ++ wpa_printf(MSG_DEBUG, "SAE: Peer " MACSTR " state=%s auth_trans=%u", ++ MAC2STR(sta->addr), sae_state_txt(sta->sae->state), ++ auth_transaction); + switch (sta->sae->state) { + case SAE_NOTHING: + if (auth_transaction == 1) { +- ret = auth_sae_send_commit(hapd, sta, bssid, 1); ++ ret = auth_sae_send_commit(hapd, sta, bssid, ++ !allow_reuse); + if (ret) + return ret; +- sta->sae->state = SAE_COMMITTED; ++ sae_set_state(sta, SAE_COMMITTED, "Sent Commit"); + + if (sae_process_commit(sta->sae) < 0) + return WLAN_STATUS_UNSPECIFIED_FAILURE; +@@ -573,7 +801,8 @@ + ret = auth_sae_send_confirm(hapd, sta, bssid); + if (ret) + return ret; +- sta->sae->state = SAE_CONFIRMED; ++ sae_set_state(sta, SAE_CONFIRMED, ++ "Sent Confirm (mesh)"); + } else { + /* + * For infrastructure BSS, send only the Commit +@@ -580,7 +809,7 @@ + * message now to get alternating sequence of + * Authentication frames between the AP and STA. + * Confirm will be sent in +- * Commited -> Confirmed/Accepted transition ++ * Committed -> Confirmed/Accepted transition + * when receiving Confirm from STA. + */ + } +@@ -602,7 +831,7 @@ + ret = auth_sae_send_confirm(hapd, sta, bssid); + if (ret) + return ret; +- sta->sae->state = SAE_CONFIRMED; ++ sae_set_state(sta, SAE_CONFIRMED, "Sent Confirm"); + sta->sae->sync = 0; + sae_set_retransmit_timer(hapd, sta); + } else if (hapd->conf->mesh & MESH_ENABLED) { +@@ -610,7 +839,7 @@ + * In mesh case, follow SAE finite state machine and + * send Commit now, if sync count allows. + */ +- if (sae_check_big_sync(sta)) ++ if (sae_check_big_sync(hapd, sta)) + return WLAN_STATUS_SUCCESS; + sta->sae->sync++; + +@@ -629,7 +858,7 @@ + if (ret) + return ret; + +- sta->sae->state = SAE_CONFIRMED; ++ sae_set_state(sta, SAE_CONFIRMED, "Sent Confirm"); + + /* + * Since this was triggered on Confirm RX, run another +@@ -636,13 +865,14 @@ + * step to get to Accepted without waiting for + * additional events. + */ +- return sae_sm_step(hapd, sta, bssid, auth_transaction); ++ return sae_sm_step(hapd, sta, bssid, auth_transaction, ++ 0, sta_removed); + } + break; + case SAE_CONFIRMED: + sae_clear_retransmit_timer(hapd, sta); + if (auth_transaction == 1) { +- if (sae_check_big_sync(sta)) ++ if (sae_check_big_sync(hapd, sta)) + return WLAN_STATUS_SUCCESS; + sta->sae->sync++; + +@@ -659,23 +889,32 @@ + + sae_set_retransmit_timer(hapd, sta); + } else { +- sta->flags |= WLAN_STA_AUTH; +- sta->auth_alg = WLAN_AUTH_SAE; +- mlme_authenticate_indication(hapd, sta); +- wpa_auth_sm_event(sta->wpa_sm, WPA_AUTH); +- sta->sae->state = SAE_ACCEPTED; +- wpa_auth_pmksa_add_sae(hapd->wpa_auth, sta->addr, +- sta->sae->pmk); ++ sta->sae->send_confirm = 0xffff; ++ sae_accept_sta(hapd, sta); + } + break; + case SAE_ACCEPTED: +- if (auth_transaction == 1) { ++ if (auth_transaction == 1 && ++ (hapd->conf->mesh & MESH_ENABLED)) { + wpa_printf(MSG_DEBUG, "SAE: remove the STA (" MACSTR + ") doing reauthentication", + MAC2STR(sta->addr)); ++ wpa_auth_pmksa_remove(hapd->wpa_auth, sta->addr); + ap_free_sta(hapd, sta); ++ *sta_removed = 1; ++ } else if (auth_transaction == 1) { ++ wpa_printf(MSG_DEBUG, "SAE: Start reauthentication"); ++ ret = auth_sae_send_commit(hapd, sta, bssid, 1); ++ if (ret) ++ return ret; ++ sae_set_state(sta, SAE_COMMITTED, "Sent Commit"); ++ ++ if (sae_process_commit(sta->sae) < 0) ++ return WLAN_STATUS_UNSPECIFIED_FAILURE; ++ sta->sae->sync = 0; ++ sae_set_retransmit_timer(hapd, sta); + } else { +- if (sae_check_big_sync(sta)) ++ if (sae_check_big_sync(hapd, sta)) + return WLAN_STATUS_SUCCESS; + sta->sae->sync++; + +@@ -694,26 +933,109 @@ + } + + ++static void sae_pick_next_group(struct hostapd_data *hapd, struct sta_info *sta) ++{ ++ struct sae_data *sae = sta->sae; ++ int i, *groups = hapd->conf->sae_groups; ++ int default_groups[] = { 19, 0 }; ++ ++ if (sae->state != SAE_COMMITTED) ++ return; ++ ++ wpa_printf(MSG_DEBUG, "SAE: Previously selected group: %d", sae->group); ++ ++ if (!groups) ++ groups = default_groups; ++ for (i = 0; groups[i] > 0; i++) { ++ if (sae->group == groups[i]) ++ break; ++ } ++ ++ if (groups[i] <= 0) { ++ wpa_printf(MSG_DEBUG, ++ "SAE: Previously selected group not found from the current configuration"); ++ return; ++ } ++ ++ for (;;) { ++ i++; ++ if (groups[i] <= 0) { ++ wpa_printf(MSG_DEBUG, ++ "SAE: No alternative group enabled"); ++ return; ++ } ++ ++ if (sae_set_group(sae, groups[i]) < 0) ++ continue; ++ ++ break; ++ } ++ wpa_printf(MSG_DEBUG, "SAE: Selected new group: %d", groups[i]); ++} ++ ++ + static void handle_auth_sae(struct hostapd_data *hapd, struct sta_info *sta, + const struct ieee80211_mgmt *mgmt, size_t len, + u16 auth_transaction, u16 status_code) + { +- u16 resp = WLAN_STATUS_SUCCESS; ++ int resp = WLAN_STATUS_SUCCESS; + struct wpabuf *data = NULL; ++ int *groups = hapd->conf->sae_groups; ++ int default_groups[] = { 19, 0 }; ++ const u8 *pos, *end; ++ int sta_removed = 0; + ++ if (!groups) ++ groups = default_groups; ++ ++#ifdef CONFIG_TESTING_OPTIONS ++ if (hapd->conf->sae_reflection_attack && auth_transaction == 1) { ++ wpa_printf(MSG_DEBUG, "SAE: TESTING - reflection attack"); ++ pos = mgmt->u.auth.variable; ++ end = ((const u8 *) mgmt) + len; ++ send_auth_reply(hapd, mgmt->sa, mgmt->bssid, WLAN_AUTH_SAE, ++ auth_transaction, resp, pos, end - pos, ++ "auth-sae-reflection-attack"); ++ goto remove_sta; ++ } ++ ++ if (hapd->conf->sae_commit_override && auth_transaction == 1) { ++ wpa_printf(MSG_DEBUG, "SAE: TESTING - commit override"); ++ send_auth_reply(hapd, mgmt->sa, mgmt->bssid, WLAN_AUTH_SAE, ++ auth_transaction, resp, ++ wpabuf_head(hapd->conf->sae_commit_override), ++ wpabuf_len(hapd->conf->sae_commit_override), ++ "sae-commit-override"); ++ goto remove_sta; ++ } ++#endif /* CONFIG_TESTING_OPTIONS */ + if (!sta->sae) { +- if (auth_transaction != 1 || status_code != WLAN_STATUS_SUCCESS) +- return; ++ if (auth_transaction != 1 || ++ status_code != WLAN_STATUS_SUCCESS) { ++ resp = -1; ++ goto remove_sta; ++ } + sta->sae = os_zalloc(sizeof(*sta->sae)); +- if (sta->sae == NULL) +- return; +- sta->sae->state = SAE_NOTHING; ++ if (!sta->sae) { ++ resp = -1; ++ goto remove_sta; ++ } ++ sae_set_state(sta, SAE_NOTHING, "Init"); + sta->sae->sync = 0; + } + ++ if (sta->mesh_sae_pmksa_caching) { ++ wpa_printf(MSG_DEBUG, ++ "SAE: Cancel use of mesh PMKSA caching because peer starts SAE authentication"); ++ wpa_auth_pmksa_remove(hapd->wpa_auth, sta->addr); ++ sta->mesh_sae_pmksa_caching = 0; ++ } ++ + if (auth_transaction == 1) { +- const u8 *token = NULL, *pos, *end; ++ const u8 *token = NULL; + size_t token_len = 0; ++ int allow_reuse = 0; ++ + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_DEBUG, + "start SAE authentication (RX commit, status=%u)", +@@ -730,8 +1052,7 @@ + resp = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto reply; + } +- resp = sae_group_allowed(sta->sae, +- hapd->conf->sae_groups, ++ resp = sae_group_allowed(sta->sae, groups, + WPA_GET_LE16(pos)); + if (resp != WLAN_STATUS_SUCCESS) { + wpa_printf(MSG_ERROR, +@@ -746,7 +1067,8 @@ + if (sta->sae->tmp->anti_clogging_token == NULL) { + wpa_printf(MSG_ERROR, + "SAE: Failed to alloc for anti-clogging token"); +- return; ++ resp = WLAN_STATUS_UNSPECIFIED_FAILURE; ++ goto remove_sta; + } + + /* +@@ -756,42 +1078,93 @@ + * Authentication frame, and the commit-scalar and + * COMMIT-ELEMENT previously sent. + */ +- if (auth_sae_send_commit(hapd, sta, mgmt->bssid, 0)) { ++ resp = auth_sae_send_commit(hapd, sta, mgmt->bssid, 0); ++ if (resp != WLAN_STATUS_SUCCESS) { + wpa_printf(MSG_ERROR, + "SAE: Failed to send commit message"); +- return; ++ goto remove_sta; + } +- sta->sae->state = SAE_COMMITTED; ++ sae_set_state(sta, SAE_COMMITTED, ++ "Sent Commit (anti-clogging token case in mesh)"); + sta->sae->sync = 0; + sae_set_retransmit_timer(hapd, sta); + return; + } + ++ if ((hapd->conf->mesh & MESH_ENABLED) && ++ status_code == ++ WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED && ++ sta->sae->tmp) { ++ wpa_printf(MSG_DEBUG, ++ "SAE: Peer did not accept our SAE group"); ++ sae_pick_next_group(hapd, sta); ++ goto remove_sta; ++ } ++ + if (status_code != WLAN_STATUS_SUCCESS) +- return; ++ goto remove_sta; + ++ if (!(hapd->conf->mesh & MESH_ENABLED) && ++ sta->sae->state == SAE_COMMITTED) { ++ /* This is needed in the infrastructure BSS case to ++ * address a sequence where a STA entry may remain in ++ * hostapd across two attempts to do SAE authentication ++ * by the same STA. The second attempt may end up trying ++ * to use a different group and that would not be ++ * allowed if we remain in Committed state with the ++ * previously set parameters. */ ++ pos = mgmt->u.auth.variable; ++ end = ((const u8 *) mgmt) + len; ++ if (end - pos >= (int) sizeof(le16) && ++ sae_group_allowed(sta->sae, groups, ++ WPA_GET_LE16(pos)) == ++ WLAN_STATUS_SUCCESS) { ++ /* Do not waste resources deriving the same PWE ++ * again since the same group is reused. */ ++ sae_set_state(sta, SAE_NOTHING, ++ "Allow previous PWE to be reused"); ++ allow_reuse = 1; ++ } else { ++ sae_set_state(sta, SAE_NOTHING, ++ "Clear existing state to allow restart"); ++ sae_clear_data(sta->sae); ++ } ++ } ++ + resp = sae_parse_commit(sta->sae, mgmt->u.auth.variable, + ((const u8 *) mgmt) + len - + mgmt->u.auth.variable, &token, +- &token_len, hapd->conf->sae_groups); ++ &token_len, groups); + if (resp == SAE_SILENTLY_DISCARD) { + wpa_printf(MSG_DEBUG, + "SAE: Drop commit message from " MACSTR " due to reflection attack", + MAC2STR(sta->addr)); +- return; ++ goto remove_sta; + } ++ ++ if (resp == WLAN_STATUS_UNKNOWN_PASSWORD_IDENTIFIER) { ++ wpa_msg(hapd->msg_ctx, MSG_INFO, ++ WPA_EVENT_SAE_UNKNOWN_PASSWORD_IDENTIFIER ++ MACSTR, MAC2STR(sta->addr)); ++ sae_clear_retransmit_timer(hapd, sta); ++ sae_set_state(sta, SAE_NOTHING, ++ "Unknown Password Identifier"); ++ goto remove_sta; ++ } ++ + if (token && check_sae_token(hapd, sta->addr, token, token_len) + < 0) { + wpa_printf(MSG_DEBUG, "SAE: Drop commit message with " + "incorrect token from " MACSTR, + MAC2STR(sta->addr)); +- return; ++ resp = WLAN_STATUS_UNSPECIFIED_FAILURE; ++ goto remove_sta; + } + + if (resp != WLAN_STATUS_SUCCESS) + goto reply; + +- if (!token && use_sae_anti_clogging(hapd)) { ++ if (!token && use_sae_anti_clogging(hapd) && !allow_reuse) { + wpa_printf(MSG_DEBUG, + "SAE: Request anti-clogging token from " + MACSTR, MAC2STR(sta->addr)); +@@ -799,11 +1172,13 @@ + sta->addr); + resp = WLAN_STATUS_ANTI_CLOGGING_TOKEN_REQ; + if (hapd->conf->mesh & MESH_ENABLED) +- sta->sae->state = SAE_NOTHING; ++ sae_set_state(sta, SAE_NOTHING, ++ "Request anti-clogging token case in mesh"); + goto reply; + } + +- resp = sae_sm_step(hapd, sta, mgmt->bssid, auth_transaction); ++ resp = sae_sm_step(hapd, sta, mgmt->bssid, auth_transaction, ++ allow_reuse, &sta_removed); + } else if (auth_transaction == 2) { + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_DEBUG, +@@ -810,17 +1185,42 @@ + "SAE authentication (RX confirm, status=%u)", + status_code); + if (status_code != WLAN_STATUS_SUCCESS) +- return; ++ goto remove_sta; + if (sta->sae->state >= SAE_CONFIRMED || + !(hapd->conf->mesh & MESH_ENABLED)) { +- if (sae_check_confirm(sta->sae, mgmt->u.auth.variable, +- ((u8 *) mgmt) + len - +- mgmt->u.auth.variable) < 0) { ++ const u8 *var; ++ size_t var_len; ++ u16 peer_send_confirm; ++ ++ var = mgmt->u.auth.variable; ++ var_len = ((u8 *) mgmt) + len - mgmt->u.auth.variable; ++ if (var_len < 2) { + resp = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto reply; + } ++ ++ peer_send_confirm = WPA_GET_LE16(var); ++ ++ if (sta->sae->state == SAE_ACCEPTED && ++ (peer_send_confirm <= sta->sae->rc || ++ peer_send_confirm == 0xffff)) { ++ wpa_printf(MSG_DEBUG, ++ "SAE: Silently ignore unexpected Confirm from peer " ++ MACSTR ++ " (peer-send-confirm=%u Rc=%u)", ++ MAC2STR(sta->addr), ++ peer_send_confirm, sta->sae->rc); ++ return; ++ } ++ ++ if (sae_check_confirm(sta->sae, var, var_len) < 0) { ++ resp = WLAN_STATUS_UNSPECIFIED_FAILURE; ++ goto reply; ++ } ++ sta->sae->rc = peer_send_confirm; + } +- resp = sae_sm_step(hapd, sta, mgmt->bssid, auth_transaction); ++ resp = sae_sm_step(hapd, sta, mgmt->bssid, auth_transaction, 0, ++ &sta_removed); + } else { + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_DEBUG, +@@ -827,17 +1227,35 @@ + "unexpected SAE authentication transaction %u (status=%u)", + auth_transaction, status_code); + if (status_code != WLAN_STATUS_SUCCESS) +- return; ++ goto remove_sta; + resp = WLAN_STATUS_UNKNOWN_AUTH_TRANSACTION; + } + + reply: +- if (resp != WLAN_STATUS_SUCCESS) { ++ if (!sta_removed && resp != WLAN_STATUS_SUCCESS) { ++ pos = mgmt->u.auth.variable; ++ end = ((const u8 *) mgmt) + len; ++ ++ /* Copy the Finite Cyclic Group field from the request if we ++ * rejected it as unsupported group. */ ++ if (resp == WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED && ++ !data && end - pos >= 2) ++ data = wpabuf_alloc_copy(pos, 2); ++ ++ sae_sme_send_external_auth_status(hapd, sta, resp); + send_auth_reply(hapd, mgmt->sa, mgmt->bssid, WLAN_AUTH_SAE, + auth_transaction, resp, + data ? wpabuf_head(data) : (u8 *) "", +- data ? wpabuf_len(data) : 0); ++ data ? wpabuf_len(data) : 0, "auth-sae"); + } ++ ++remove_sta: ++ if (!sta_removed && sta->added_unassoc && ++ (resp != WLAN_STATUS_SUCCESS || ++ status_code != WLAN_STATUS_SUCCESS)) { ++ hostapd_drv_sta_remove(hapd, sta->addr); ++ sta->added_unassoc = 0; ++ } + wpabuf_free(data); + } + +@@ -866,7 +1284,7 @@ + if (ret) + return -1; + +- sta->sae->state = SAE_COMMITTED; ++ sae_set_state(sta, SAE_COMMITTED, "Init and sent commit"); + sta->sae->sync = 0; + sae_set_retransmit_timer(hapd, sta); + +@@ -873,20 +1291,749 @@ + return 0; + } + ++ ++void auth_sae_process_commit(void *eloop_ctx, void *user_ctx) ++{ ++ struct hostapd_data *hapd = eloop_ctx; ++ struct hostapd_sae_commit_queue *q; ++ unsigned int queue_len; ++ ++ q = dl_list_first(&hapd->sae_commit_queue, ++ struct hostapd_sae_commit_queue, list); ++ if (!q) ++ return; ++ wpa_printf(MSG_DEBUG, ++ "SAE: Process next available message from queue"); ++ dl_list_del(&q->list); ++ handle_auth(hapd, (const struct ieee80211_mgmt *) q->msg, q->len, ++ q->rssi, 1); ++ os_free(q); ++ ++ if (eloop_is_timeout_registered(auth_sae_process_commit, hapd, NULL)) ++ return; ++ queue_len = dl_list_len(&hapd->sae_commit_queue); ++ eloop_register_timeout(0, queue_len * 10000, auth_sae_process_commit, ++ hapd, NULL); ++} ++ ++ ++static void auth_sae_queue(struct hostapd_data *hapd, ++ const struct ieee80211_mgmt *mgmt, size_t len, ++ int rssi) ++{ ++ struct hostapd_sae_commit_queue *q, *q2; ++ unsigned int queue_len; ++ const struct ieee80211_mgmt *mgmt2; ++ ++ queue_len = dl_list_len(&hapd->sae_commit_queue); ++ if (queue_len >= 15) { ++ wpa_printf(MSG_DEBUG, ++ "SAE: No more room in message queue - drop the new frame from " ++ MACSTR, MAC2STR(mgmt->sa)); ++ return; ++ } ++ ++ wpa_printf(MSG_DEBUG, "SAE: Queue Authentication message from " ++ MACSTR " for processing (queue_len %u)", MAC2STR(mgmt->sa), ++ queue_len); ++ q = os_zalloc(sizeof(*q) + len); ++ if (!q) ++ return; ++ q->rssi = rssi; ++ q->len = len; ++ os_memcpy(q->msg, mgmt, len); ++ ++ /* Check whether there is already a queued Authentication frame from the ++ * same station with the same transaction number and if so, replace that ++ * queue entry with the new one. This avoids issues with a peer that ++ * sends multiple times (e.g., due to frequent SAE retries). There is no ++ * point in us trying to process the old attempts after a new one has ++ * obsoleted them. */ ++ dl_list_for_each(q2, &hapd->sae_commit_queue, ++ struct hostapd_sae_commit_queue, list) { ++ mgmt2 = (const struct ieee80211_mgmt *) q2->msg; ++ if (os_memcmp(mgmt->sa, mgmt2->sa, ETH_ALEN) == 0 && ++ mgmt->u.auth.auth_transaction == ++ mgmt2->u.auth.auth_transaction) { ++ wpa_printf(MSG_DEBUG, ++ "SAE: Replace queued message from same STA with same transaction number"); ++ dl_list_add(&q2->list, &q->list); ++ dl_list_del(&q2->list); ++ os_free(q2); ++ goto queued; ++ } ++ } ++ ++ /* No pending identical entry, so add to the end of the queue */ ++ dl_list_add_tail(&hapd->sae_commit_queue, &q->list); ++ ++queued: ++ if (eloop_is_timeout_registered(auth_sae_process_commit, hapd, NULL)) ++ return; ++ eloop_register_timeout(0, queue_len * 10000, auth_sae_process_commit, ++ hapd, NULL); ++} ++ ++ ++static int auth_sae_queued_addr(struct hostapd_data *hapd, const u8 *addr) ++{ ++ struct hostapd_sae_commit_queue *q; ++ const struct ieee80211_mgmt *mgmt; ++ ++ dl_list_for_each(q, &hapd->sae_commit_queue, ++ struct hostapd_sae_commit_queue, list) { ++ mgmt = (const struct ieee80211_mgmt *) q->msg; ++ if (os_memcmp(addr, mgmt->sa, ETH_ALEN) == 0) ++ return 1; ++ } ++ ++ return 0; ++} ++ + #endif /* CONFIG_SAE */ + + ++static u16 wpa_res_to_status_code(int res) ++{ ++ if (res == WPA_INVALID_GROUP) ++ return WLAN_STATUS_GROUP_CIPHER_NOT_VALID; ++ if (res == WPA_INVALID_PAIRWISE) ++ return WLAN_STATUS_PAIRWISE_CIPHER_NOT_VALID; ++ if (res == WPA_INVALID_AKMP) ++ return WLAN_STATUS_AKMP_NOT_VALID; ++ if (res == WPA_ALLOC_FAIL) ++ return WLAN_STATUS_UNSPECIFIED_FAILURE; ++#ifdef CONFIG_IEEE80211W ++ if (res == WPA_MGMT_FRAME_PROTECTION_VIOLATION) ++ return WLAN_STATUS_ROBUST_MGMT_FRAME_POLICY_VIOLATION; ++ if (res == WPA_INVALID_MGMT_GROUP_CIPHER) ++ return WLAN_STATUS_CIPHER_REJECTED_PER_POLICY; ++#endif /* CONFIG_IEEE80211W */ ++ if (res == WPA_INVALID_MDIE) ++ return WLAN_STATUS_INVALID_MDIE; ++ if (res == WPA_INVALID_PMKID) ++ return WLAN_STATUS_INVALID_PMKID; ++ if (res != WPA_IE_OK) ++ return WLAN_STATUS_INVALID_IE; ++ return WLAN_STATUS_SUCCESS; ++} ++ ++ ++#ifdef CONFIG_FILS ++ ++static void handle_auth_fils_finish(struct hostapd_data *hapd, ++ struct sta_info *sta, u16 resp, ++ struct wpabuf *data, int pub); ++ ++void handle_auth_fils(struct hostapd_data *hapd, struct sta_info *sta, ++ const u8 *pos, size_t len, u16 auth_alg, ++ u16 auth_transaction, u16 status_code, ++ void (*cb)(struct hostapd_data *hapd, ++ struct sta_info *sta, u16 resp, ++ struct wpabuf *data, int pub)) ++{ ++ u16 resp = WLAN_STATUS_SUCCESS; ++ const u8 *end; ++ struct ieee802_11_elems elems; ++ int res; ++ struct wpa_ie_data rsn; ++ struct rsn_pmksa_cache_entry *pmksa = NULL; ++ ++ if (auth_transaction != 1 || status_code != WLAN_STATUS_SUCCESS) ++ return; ++ ++ end = pos + len; ++ ++ wpa_hexdump(MSG_DEBUG, "FILS: Authentication frame fields", ++ pos, end - pos); ++ ++ /* TODO: FILS PK */ ++#ifdef CONFIG_FILS_SK_PFS ++ if (auth_alg == WLAN_AUTH_FILS_SK_PFS) { ++ u16 group; ++ struct wpabuf *pub; ++ size_t elem_len; ++ ++ /* Using FILS PFS */ ++ ++ /* Finite Cyclic Group */ ++ if (end - pos < 2) { ++ wpa_printf(MSG_DEBUG, ++ "FILS: No room for Finite Cyclic Group"); ++ resp = WLAN_STATUS_UNSPECIFIED_FAILURE; ++ goto fail; ++ } ++ group = WPA_GET_LE16(pos); ++ pos += 2; ++ if (group != hapd->conf->fils_dh_group) { ++ wpa_printf(MSG_DEBUG, ++ "FILS: Unsupported Finite Cyclic Group: %u (expected %u)", ++ group, hapd->conf->fils_dh_group); ++ resp = WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED; ++ goto fail; ++ } ++ ++ crypto_ecdh_deinit(sta->fils_ecdh); ++ sta->fils_ecdh = crypto_ecdh_init(group); ++ if (!sta->fils_ecdh) { ++ wpa_printf(MSG_INFO, ++ "FILS: Could not initialize ECDH with group %d", ++ group); ++ resp = WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED; ++ goto fail; ++ } ++ ++ pub = crypto_ecdh_get_pubkey(sta->fils_ecdh, 1); ++ if (!pub) { ++ wpa_printf(MSG_DEBUG, ++ "FILS: Failed to derive ECDH public key"); ++ resp = WLAN_STATUS_UNSPECIFIED_FAILURE; ++ goto fail; ++ } ++ elem_len = wpabuf_len(pub); ++ wpabuf_free(pub); ++ ++ /* Element */ ++ if ((size_t) (end - pos) < elem_len) { ++ wpa_printf(MSG_DEBUG, "FILS: No room for Element"); ++ resp = WLAN_STATUS_UNSPECIFIED_FAILURE; ++ goto fail; ++ } ++ ++ wpabuf_free(sta->fils_g_sta); ++ sta->fils_g_sta = wpabuf_alloc_copy(pos, elem_len); ++ wpabuf_clear_free(sta->fils_dh_ss); ++ sta->fils_dh_ss = crypto_ecdh_set_peerkey(sta->fils_ecdh, 1, ++ pos, elem_len); ++ if (!sta->fils_dh_ss) { ++ wpa_printf(MSG_DEBUG, "FILS: ECDH operation failed"); ++ resp = WLAN_STATUS_UNSPECIFIED_FAILURE; ++ goto fail; ++ } ++ wpa_hexdump_buf_key(MSG_DEBUG, "FILS: DH_SS", sta->fils_dh_ss); ++ pos += elem_len; ++ } else { ++ crypto_ecdh_deinit(sta->fils_ecdh); ++ sta->fils_ecdh = NULL; ++ wpabuf_clear_free(sta->fils_dh_ss); ++ sta->fils_dh_ss = NULL; ++ } ++#endif /* CONFIG_FILS_SK_PFS */ ++ ++ wpa_hexdump(MSG_DEBUG, "FILS: Remaining IEs", pos, end - pos); ++ if (ieee802_11_parse_elems(pos, end - pos, &elems, 1) == ParseFailed) { ++ wpa_printf(MSG_DEBUG, "FILS: Could not parse elements"); ++ resp = WLAN_STATUS_UNSPECIFIED_FAILURE; ++ goto fail; ++ } ++ ++ /* RSNE */ ++ wpa_hexdump(MSG_DEBUG, "FILS: RSN element", ++ elems.rsn_ie, elems.rsn_ie_len); ++ if (!elems.rsn_ie || ++ wpa_parse_wpa_ie_rsn(elems.rsn_ie - 2, elems.rsn_ie_len + 2, ++ &rsn) < 0) { ++ wpa_printf(MSG_DEBUG, "FILS: No valid RSN element"); ++ resp = WLAN_STATUS_UNSPECIFIED_FAILURE; ++ goto fail; ++ } ++ ++ if (!sta->wpa_sm) ++ sta->wpa_sm = wpa_auth_sta_init(hapd->wpa_auth, sta->addr, ++ NULL); ++ if (!sta->wpa_sm) { ++ wpa_printf(MSG_DEBUG, ++ "FILS: Failed to initialize RSN state machine"); ++ resp = WLAN_STATUS_UNSPECIFIED_FAILURE; ++ goto fail; ++ } ++ ++ res = wpa_validate_wpa_ie(hapd->wpa_auth, sta->wpa_sm, ++ hapd->iface->freq, ++ elems.rsn_ie - 2, elems.rsn_ie_len + 2, ++ elems.mdie, elems.mdie_len, NULL, 0); ++ resp = wpa_res_to_status_code(res); ++ if (resp != WLAN_STATUS_SUCCESS) ++ goto fail; ++ ++ if (!elems.fils_nonce) { ++ wpa_printf(MSG_DEBUG, "FILS: No FILS Nonce field"); ++ resp = WLAN_STATUS_UNSPECIFIED_FAILURE; ++ goto fail; ++ } ++ wpa_hexdump(MSG_DEBUG, "FILS: SNonce", elems.fils_nonce, ++ FILS_NONCE_LEN); ++ os_memcpy(sta->fils_snonce, elems.fils_nonce, FILS_NONCE_LEN); ++ ++ /* PMKID List */ ++ if (rsn.pmkid && rsn.num_pmkid > 0) { ++ u8 num; ++ const u8 *pmkid; ++ ++ wpa_hexdump(MSG_DEBUG, "FILS: PMKID List", ++ rsn.pmkid, rsn.num_pmkid * PMKID_LEN); ++ ++ pmkid = rsn.pmkid; ++ num = rsn.num_pmkid; ++ while (num) { ++ wpa_hexdump(MSG_DEBUG, "FILS: PMKID", pmkid, PMKID_LEN); ++ pmksa = wpa_auth_pmksa_get(hapd->wpa_auth, sta->addr, ++ pmkid); ++ if (pmksa) ++ break; ++ pmksa = wpa_auth_pmksa_get_fils_cache_id(hapd->wpa_auth, ++ sta->addr, ++ pmkid); ++ if (pmksa) ++ break; ++ pmkid += PMKID_LEN; ++ num--; ++ } ++ } ++ if (pmksa && wpa_auth_sta_key_mgmt(sta->wpa_sm) != pmksa->akmp) { ++ wpa_printf(MSG_DEBUG, ++ "FILS: Matching PMKSA cache entry has different AKMP (0x%x != 0x%x) - ignore", ++ wpa_auth_sta_key_mgmt(sta->wpa_sm), pmksa->akmp); ++ pmksa = NULL; ++ } ++ if (pmksa) ++ wpa_printf(MSG_DEBUG, "FILS: Found matching PMKSA cache entry"); ++ ++ /* FILS Session */ ++ if (!elems.fils_session) { ++ wpa_printf(MSG_DEBUG, "FILS: No FILS Session element"); ++ resp = WLAN_STATUS_UNSPECIFIED_FAILURE; ++ goto fail; ++ } ++ wpa_hexdump(MSG_DEBUG, "FILS: FILS Session", elems.fils_session, ++ FILS_SESSION_LEN); ++ os_memcpy(sta->fils_session, elems.fils_session, FILS_SESSION_LEN); ++ ++ /* FILS Wrapped Data */ ++ if (elems.fils_wrapped_data) { ++ wpa_hexdump(MSG_DEBUG, "FILS: Wrapped Data", ++ elems.fils_wrapped_data, ++ elems.fils_wrapped_data_len); ++ if (!pmksa) { ++#ifndef CONFIG_NO_RADIUS ++ if (!sta->eapol_sm) { ++ sta->eapol_sm = ++ ieee802_1x_alloc_eapol_sm(hapd, sta); ++ } ++ wpa_printf(MSG_DEBUG, ++ "FILS: Forward EAP-Initiate/Re-auth to authentication server"); ++ ieee802_1x_encapsulate_radius( ++ hapd, sta, elems.fils_wrapped_data, ++ elems.fils_wrapped_data_len); ++ sta->fils_pending_cb = cb; ++ wpa_printf(MSG_DEBUG, ++ "FILS: Will send Authentication frame once the response from authentication server is available"); ++ sta->flags |= WLAN_STA_PENDING_FILS_ERP; ++ /* Calculate pending PMKID here so that we do not need ++ * to maintain a copy of the EAP-Initiate/Reauth ++ * message. */ ++ if (fils_pmkid_erp(wpa_auth_sta_key_mgmt(sta->wpa_sm), ++ elems.fils_wrapped_data, ++ elems.fils_wrapped_data_len, ++ sta->fils_erp_pmkid) == 0) ++ sta->fils_erp_pmkid_set = 1; ++ return; ++#else /* CONFIG_NO_RADIUS */ ++ resp = WLAN_STATUS_UNSPECIFIED_FAILURE; ++ goto fail; ++#endif /* CONFIG_NO_RADIUS */ ++ } ++ } ++ ++fail: ++ if (cb) { ++ struct wpabuf *data; ++ int pub = 0; ++ ++ data = prepare_auth_resp_fils(hapd, sta, &resp, pmksa, NULL, ++ NULL, 0, &pub); ++ if (!data) { ++ wpa_printf(MSG_DEBUG, ++ "%s: prepare_auth_resp_fils() returned failure", ++ __func__); ++ } ++ ++ cb(hapd, sta, resp, data, pub); ++ } ++} ++ ++ ++static struct wpabuf * ++prepare_auth_resp_fils(struct hostapd_data *hapd, ++ struct sta_info *sta, u16 *resp, ++ struct rsn_pmksa_cache_entry *pmksa, ++ struct wpabuf *erp_resp, ++ const u8 *msk, size_t msk_len, ++ int *is_pub) ++{ ++ u8 fils_nonce[FILS_NONCE_LEN]; ++ size_t ielen; ++ struct wpabuf *data = NULL; ++ const u8 *ie; ++ u8 *ie_buf = NULL; ++ const u8 *pmk = NULL; ++ size_t pmk_len = 0; ++ u8 pmk_buf[PMK_LEN_MAX]; ++ struct wpabuf *pub = NULL; ++ ++ if (*resp != WLAN_STATUS_SUCCESS) ++ goto fail; ++ ++ ie = wpa_auth_get_wpa_ie(hapd->wpa_auth, &ielen); ++ if (!ie) { ++ *resp = WLAN_STATUS_UNSPECIFIED_FAILURE; ++ goto fail; ++ } ++ ++ if (pmksa) { ++ /* Add PMKID of the selected PMKSA into RSNE */ ++ ie_buf = os_malloc(ielen + 2 + 2 + PMKID_LEN); ++ if (!ie_buf) { ++ *resp = WLAN_STATUS_UNSPECIFIED_FAILURE; ++ goto fail; ++ } ++ ++ os_memcpy(ie_buf, ie, ielen); ++ if (wpa_insert_pmkid(ie_buf, &ielen, pmksa->pmkid) < 0) { ++ *resp = WLAN_STATUS_UNSPECIFIED_FAILURE; ++ goto fail; ++ } ++ ie = ie_buf; ++ } ++ ++ if (random_get_bytes(fils_nonce, FILS_NONCE_LEN) < 0) { ++ *resp = WLAN_STATUS_UNSPECIFIED_FAILURE; ++ goto fail; ++ } ++ wpa_hexdump(MSG_DEBUG, "RSN: Generated FILS Nonce", ++ fils_nonce, FILS_NONCE_LEN); ++ ++#ifdef CONFIG_FILS_SK_PFS ++ if (sta->fils_dh_ss && sta->fils_ecdh) { ++ pub = crypto_ecdh_get_pubkey(sta->fils_ecdh, 1); ++ if (!pub) { ++ *resp = WLAN_STATUS_UNSPECIFIED_FAILURE; ++ goto fail; ++ } ++ } ++#endif /* CONFIG_FILS_SK_PFS */ ++ ++ data = wpabuf_alloc(1000 + ielen + (pub ? wpabuf_len(pub) : 0)); ++ if (!data) { ++ *resp = WLAN_STATUS_UNSPECIFIED_FAILURE; ++ goto fail; ++ } ++ ++ /* TODO: FILS PK */ ++#ifdef CONFIG_FILS_SK_PFS ++ if (pub) { ++ /* Finite Cyclic Group */ ++ wpabuf_put_le16(data, hapd->conf->fils_dh_group); ++ ++ /* Element */ ++ wpabuf_put_buf(data, pub); ++ } ++#endif /* CONFIG_FILS_SK_PFS */ ++ ++ /* RSNE */ ++ wpabuf_put_data(data, ie, ielen); ++ ++ /* MDE when using FILS+FT (already included in ie,ielen with RSNE) */ ++ ++#ifdef CONFIG_IEEE80211R_AP ++ if (wpa_key_mgmt_ft(wpa_auth_sta_key_mgmt(sta->wpa_sm))) { ++ /* FTE[R1KH-ID,R0KH-ID] when using FILS+FT */ ++ int res; ++ int use_sha384 = wpa_key_mgmt_sha384( ++ wpa_auth_sta_key_mgmt(sta->wpa_sm)); ++ ++ res = wpa_auth_write_fte(hapd->wpa_auth, use_sha384, ++ wpabuf_put(data, 0), ++ wpabuf_tailroom(data)); ++ if (res < 0) { ++ *resp = WLAN_STATUS_UNSPECIFIED_FAILURE; ++ goto fail; ++ } ++ wpabuf_put(data, res); ++ } ++#endif /* CONFIG_IEEE80211R_AP */ ++ ++ /* FILS Nonce */ ++ wpabuf_put_u8(data, WLAN_EID_EXTENSION); /* Element ID */ ++ wpabuf_put_u8(data, 1 + FILS_NONCE_LEN); /* Length */ ++ /* Element ID Extension */ ++ wpabuf_put_u8(data, WLAN_EID_EXT_FILS_NONCE); ++ wpabuf_put_data(data, fils_nonce, FILS_NONCE_LEN); ++ ++ /* FILS Session */ ++ wpabuf_put_u8(data, WLAN_EID_EXTENSION); /* Element ID */ ++ wpabuf_put_u8(data, 1 + FILS_SESSION_LEN); /* Length */ ++ /* Element ID Extension */ ++ wpabuf_put_u8(data, WLAN_EID_EXT_FILS_SESSION); ++ wpabuf_put_data(data, sta->fils_session, FILS_SESSION_LEN); ++ ++ /* FILS Wrapped Data */ ++ if (!pmksa && erp_resp) { ++ wpabuf_put_u8(data, WLAN_EID_EXTENSION); /* Element ID */ ++ wpabuf_put_u8(data, 1 + wpabuf_len(erp_resp)); /* Length */ ++ /* Element ID Extension */ ++ wpabuf_put_u8(data, WLAN_EID_EXT_FILS_WRAPPED_DATA); ++ wpabuf_put_buf(data, erp_resp); ++ ++ if (fils_rmsk_to_pmk(wpa_auth_sta_key_mgmt(sta->wpa_sm), ++ msk, msk_len, sta->fils_snonce, fils_nonce, ++ sta->fils_dh_ss ? ++ wpabuf_head(sta->fils_dh_ss) : NULL, ++ sta->fils_dh_ss ? ++ wpabuf_len(sta->fils_dh_ss) : 0, ++ pmk_buf, &pmk_len)) { ++ wpa_printf(MSG_DEBUG, "FILS: Failed to derive PMK"); ++ *resp = WLAN_STATUS_UNSPECIFIED_FAILURE; ++ wpabuf_free(data); ++ data = NULL; ++ goto fail; ++ } ++ pmk = pmk_buf; ++ ++ /* Don't use DHss in PTK derivation if PMKSA caching is not ++ * used. */ ++ wpabuf_clear_free(sta->fils_dh_ss); ++ sta->fils_dh_ss = NULL; ++ ++ if (sta->fils_erp_pmkid_set) { ++ /* TODO: get PMKLifetime from WPA parameters */ ++ unsigned int dot11RSNAConfigPMKLifetime = 43200; ++ int session_timeout; ++ ++ session_timeout = dot11RSNAConfigPMKLifetime; ++ if (sta->session_timeout_set) { ++ struct os_reltime now, diff; ++ ++ os_get_reltime(&now); ++ os_reltime_sub(&sta->session_timeout, &now, ++ &diff); ++ session_timeout = diff.sec; ++ } ++ ++ sta->fils_erp_pmkid_set = 0; ++ wpa_auth_add_fils_pmk_pmkid(sta->wpa_sm, pmk, pmk_len, ++ sta->fils_erp_pmkid); ++ if (!hapd->conf->disable_pmksa_caching && ++ wpa_auth_pmksa_add2( ++ hapd->wpa_auth, sta->addr, ++ pmk, pmk_len, ++ sta->fils_erp_pmkid, ++ session_timeout, ++ wpa_auth_sta_key_mgmt(sta->wpa_sm)) < 0) { ++ wpa_printf(MSG_ERROR, ++ "FILS: Failed to add PMKSA cache entry based on ERP"); ++ } ++ } ++ } else if (pmksa) { ++ pmk = pmksa->pmk; ++ pmk_len = pmksa->pmk_len; ++ } ++ ++ if (!pmk) { ++ wpa_printf(MSG_DEBUG, "FILS: No PMK available"); ++ *resp = WLAN_STATUS_UNSPECIFIED_FAILURE; ++ wpabuf_free(data); ++ data = NULL; ++ goto fail; ++ } ++ ++ if (fils_auth_pmk_to_ptk(sta->wpa_sm, pmk, pmk_len, ++ sta->fils_snonce, fils_nonce, ++ sta->fils_dh_ss ? ++ wpabuf_head(sta->fils_dh_ss) : NULL, ++ sta->fils_dh_ss ? ++ wpabuf_len(sta->fils_dh_ss) : 0, ++ sta->fils_g_sta, pub) < 0) { ++ *resp = WLAN_STATUS_UNSPECIFIED_FAILURE; ++ wpabuf_free(data); ++ data = NULL; ++ goto fail; ++ } ++ ++fail: ++ if (is_pub) ++ *is_pub = pub != NULL; ++ os_free(ie_buf); ++ wpabuf_free(pub); ++ wpabuf_clear_free(sta->fils_dh_ss); ++ sta->fils_dh_ss = NULL; ++#ifdef CONFIG_FILS_SK_PFS ++ crypto_ecdh_deinit(sta->fils_ecdh); ++ sta->fils_ecdh = NULL; ++#endif /* CONFIG_FILS_SK_PFS */ ++ return data; ++} ++ ++ ++static void handle_auth_fils_finish(struct hostapd_data *hapd, ++ struct sta_info *sta, u16 resp, ++ struct wpabuf *data, int pub) ++{ ++ u16 auth_alg; ++ ++ auth_alg = (pub || ++ resp == WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED) ? ++ WLAN_AUTH_FILS_SK_PFS : WLAN_AUTH_FILS_SK; ++ send_auth_reply(hapd, sta->addr, hapd->own_addr, auth_alg, 2, resp, ++ data ? wpabuf_head(data) : (u8 *) "", ++ data ? wpabuf_len(data) : 0, "auth-fils-finish"); ++ wpabuf_free(data); ++ ++ if (resp == WLAN_STATUS_SUCCESS) { ++ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, ++ HOSTAPD_LEVEL_DEBUG, ++ "authentication OK (FILS)"); ++ sta->flags |= WLAN_STA_AUTH; ++ wpa_auth_sm_event(sta->wpa_sm, WPA_AUTH); ++ sta->auth_alg = pub ? WLAN_AUTH_FILS_SK_PFS : WLAN_AUTH_FILS_SK; ++ mlme_authenticate_indication(hapd, sta); ++ } ++} ++ ++ ++void ieee802_11_finish_fils_auth(struct hostapd_data *hapd, ++ struct sta_info *sta, int success, ++ struct wpabuf *erp_resp, ++ const u8 *msk, size_t msk_len) ++{ ++ struct wpabuf *data; ++ int pub = 0; ++ u16 resp; ++ ++ sta->flags &= ~WLAN_STA_PENDING_FILS_ERP; ++ ++ if (!sta->fils_pending_cb) ++ return; ++ resp = success ? WLAN_STATUS_SUCCESS : WLAN_STATUS_UNSPECIFIED_FAILURE; ++ data = prepare_auth_resp_fils(hapd, sta, &resp, NULL, erp_resp, ++ msk, msk_len, &pub); ++ if (!data) { ++ wpa_printf(MSG_DEBUG, ++ "%s: prepare_auth_resp_fils() returned failure", ++ __func__); ++ } ++ sta->fils_pending_cb(hapd, sta, resp, data, pub); ++} ++ ++#endif /* CONFIG_FILS */ ++ ++ ++int ++ieee802_11_allowed_address(struct hostapd_data *hapd, const u8 *addr, ++ const u8 *msg, size_t len, u32 *session_timeout, ++ u32 *acct_interim_interval, ++ struct vlan_description *vlan_id, ++ struct hostapd_sta_wpa_psk_short **psk, ++ char **identity, char **radius_cui, int is_probe_req) ++{ ++ int res; ++ ++ os_memset(vlan_id, 0, sizeof(*vlan_id)); ++ res = hostapd_allowed_address(hapd, addr, msg, len, ++ session_timeout, acct_interim_interval, ++ vlan_id, psk, identity, radius_cui, ++ is_probe_req); ++ ++ if (res == HOSTAPD_ACL_REJECT) { ++ if (!is_probe_req) ++ wpa_printf(MSG_DEBUG, ++ "Station " MACSTR ++ " not allowed to authenticate", ++ MAC2STR(addr)); ++ return HOSTAPD_ACL_REJECT; ++ } ++ ++ if (res == HOSTAPD_ACL_PENDING) { ++ wpa_printf(MSG_DEBUG, "Authentication frame from " MACSTR ++ " waiting for an external authentication", ++ MAC2STR(addr)); ++ /* Authentication code will re-send the authentication frame ++ * after it has received (and cached) information from the ++ * external source. */ ++ return HOSTAPD_ACL_PENDING; ++ } ++ ++ return res; ++} ++ ++ ++static int ++ieee802_11_set_radius_info(struct hostapd_data *hapd, struct sta_info *sta, ++ int res, u32 session_timeout, ++ u32 acct_interim_interval, ++ struct vlan_description *vlan_id, ++ struct hostapd_sta_wpa_psk_short **psk, ++ char **identity, char **radius_cui) ++{ ++ if (vlan_id->notempty && ++ !hostapd_vlan_valid(hapd->conf->vlan, vlan_id)) { ++ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_RADIUS, ++ HOSTAPD_LEVEL_INFO, ++ "Invalid VLAN %d%s received from RADIUS server", ++ vlan_id->untagged, ++ vlan_id->tagged[0] ? "+" : ""); ++ return -1; ++ } ++ if (ap_sta_set_vlan(hapd, sta, vlan_id) < 0) ++ return -1; ++ if (sta->vlan_id) ++ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_RADIUS, ++ HOSTAPD_LEVEL_INFO, "VLAN ID %d", sta->vlan_id); ++ ++ hostapd_free_psk_list(sta->psk); ++ if (hapd->conf->wpa_psk_radius != PSK_RADIUS_IGNORED) { ++ sta->psk = *psk; ++ *psk = NULL; ++ } else { ++ sta->psk = NULL; ++ } ++ ++ os_free(sta->identity); ++ sta->identity = *identity; ++ *identity = NULL; ++ ++ os_free(sta->radius_cui); ++ sta->radius_cui = *radius_cui; ++ *radius_cui = NULL; ++ ++ if (hapd->conf->acct_interim_interval == 0 && acct_interim_interval) ++ sta->acct_interim_interval = acct_interim_interval; ++ if (res == HOSTAPD_ACL_ACCEPT_TIMEOUT) { ++ sta->session_timeout_set = 1; ++ os_get_reltime(&sta->session_timeout); ++ sta->session_timeout.sec += session_timeout; ++ ap_sta_session_timeout(hapd, sta, session_timeout); ++ } else { ++ sta->session_timeout_set = 0; ++ ap_sta_no_session_timeout(hapd, sta); ++ } ++ ++ return 0; ++} ++ ++ + static void handle_auth(struct hostapd_data *hapd, +- const struct ieee80211_mgmt *mgmt, size_t len) ++ const struct ieee80211_mgmt *mgmt, size_t len, ++ int rssi, int from_queue) + { + u16 auth_alg, auth_transaction, status_code; + u16 resp = WLAN_STATUS_SUCCESS; + struct sta_info *sta = NULL; +- int res; ++ int res, reply_res; + u16 fc; + const u8 *challenge = NULL; + u32 session_timeout, acct_interim_interval; +- int vlan_id = 0; ++ struct vlan_description vlan_id; + struct hostapd_sta_wpa_psk_short *psk = NULL; + u8 resp_ies[2 + WLAN_AUTH_CHALLENGE_LEN]; + size_t resp_ies_len = 0; +@@ -924,11 +2071,12 @@ + + wpa_printf(MSG_DEBUG, "authentication: STA=" MACSTR " auth_alg=%d " + "auth_transaction=%d status_code=%d wep=%d%s " +- "seq_ctrl=0x%x%s", ++ "seq_ctrl=0x%x%s%s", + MAC2STR(mgmt->sa), auth_alg, auth_transaction, + status_code, !!(fc & WLAN_FC_ISWEP), + challenge ? " challenge" : "", +- seq_ctrl, (fc & WLAN_FC_RETRY) ? " retry" : ""); ++ seq_ctrl, (fc & WLAN_FC_RETRY) ? " retry" : "", ++ from_queue ? " (from queue)" : ""); + + #ifdef CONFIG_NO_RC4 + if (auth_alg == WLAN_AUTH_SHARED_KEY) { +@@ -941,20 +2089,29 @@ + #endif /* CONFIG_NO_RC4 */ + + if (hapd->tkip_countermeasures) { +- resp = WLAN_REASON_MICHAEL_MIC_FAILURE; ++ wpa_printf(MSG_DEBUG, ++ "Ongoing TKIP countermeasures (Michael MIC failure) - reject authentication"); ++ resp = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto fail; + } + + if (!(((hapd->conf->auth_algs & WPA_AUTH_ALG_OPEN) && + auth_alg == WLAN_AUTH_OPEN) || +-#ifdef CONFIG_IEEE80211R ++#ifdef CONFIG_IEEE80211R_AP + (hapd->conf->wpa && wpa_key_mgmt_ft(hapd->conf->wpa_key_mgmt) && + auth_alg == WLAN_AUTH_FT) || +-#endif /* CONFIG_IEEE80211R */ ++#endif /* CONFIG_IEEE80211R_AP */ + #ifdef CONFIG_SAE + (hapd->conf->wpa && wpa_key_mgmt_sae(hapd->conf->wpa_key_mgmt) && + auth_alg == WLAN_AUTH_SAE) || + #endif /* CONFIG_SAE */ ++#ifdef CONFIG_FILS ++ (hapd->conf->wpa && wpa_key_mgmt_fils(hapd->conf->wpa_key_mgmt) && ++ auth_alg == WLAN_AUTH_FILS_SK) || ++ (hapd->conf->wpa && wpa_key_mgmt_fils(hapd->conf->wpa_key_mgmt) && ++ hapd->conf->fils_dh_group && ++ auth_alg == WLAN_AUTH_FILS_SK_PFS) || ++#endif /* CONFIG_FILS */ + ((hapd->conf->auth_algs & WPA_AUTH_ALG_SHARED) && + auth_alg == WLAN_AUTH_SHARED_KEY))) { + wpa_printf(MSG_INFO, "Unsupported authentication algorithm (%d)", +@@ -1033,29 +2190,40 @@ + } + } + +- res = hostapd_allowed_address(hapd, mgmt->sa, (u8 *) mgmt, len, +- &session_timeout, +- &acct_interim_interval, &vlan_id, +- &psk, &identity, &radius_cui); +- ++ res = ieee802_11_allowed_address( ++ hapd, mgmt->sa, (const u8 *) mgmt, len, &session_timeout, ++ &acct_interim_interval, &vlan_id, &psk, &identity, &radius_cui, ++ 0); + if (res == HOSTAPD_ACL_REJECT) { +- wpa_printf(MSG_INFO, "Station " MACSTR " not allowed to authenticate", +- MAC2STR(mgmt->sa)); ++ wpa_msg(hapd->msg_ctx, MSG_DEBUG, ++ "Ignore Authentication frame from " MACSTR ++ " due to ACL reject", MAC2STR(mgmt->sa)); + resp = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto fail; + } +- if (res == HOSTAPD_ACL_PENDING) { +- wpa_printf(MSG_DEBUG, "Authentication frame from " MACSTR +- " waiting for an external authentication", +- MAC2STR(mgmt->sa)); +- /* Authentication code will re-send the authentication frame +- * after it has received (and cached) information from the +- * external source. */ ++ if (res == HOSTAPD_ACL_PENDING) + return; ++ ++#ifdef CONFIG_SAE ++ if (auth_alg == WLAN_AUTH_SAE && !from_queue && ++ (auth_transaction == 1 || ++ (auth_transaction == 2 && auth_sae_queued_addr(hapd, mgmt->sa)))) { ++ /* Handle SAE Authentication commit message through a queue to ++ * provide more control for postponing the needed heavy ++ * processing under a possible DoS attack scenario. In addition, ++ * queue SAE Authentication confirm message if there happens to ++ * be a queued commit message from the same peer. This is needed ++ * to avoid reordering Authentication frames within the same ++ * SAE exchange. */ ++ auth_sae_queue(hapd, mgmt, len, rssi); ++ return; + } ++#endif /* CONFIG_SAE */ + + sta = ap_get_sta(hapd, mgmt->sa); + if (sta) { ++ sta->flags &= ~WLAN_STA_PENDING_FILS_ERP; ++ sta->ft_over_ds = 0; + if ((fc & WLAN_FC_RETRY) && + sta->last_seq_ctrl != WLAN_INVALID_MGMT_SEQ && + sta->last_seq_ctrl == seq_ctrl && +@@ -1067,6 +2235,15 @@ + seq_ctrl); + return; + } ++#ifdef CONFIG_MESH ++ if ((hapd->conf->mesh & MESH_ENABLED) && ++ sta->plink_state == PLINK_BLOCKED) { ++ wpa_printf(MSG_DEBUG, "Mesh peer " MACSTR ++ " is blocked - drop Authentication frame", ++ MAC2STR(mgmt->sa)); ++ return; ++ } ++#endif /* CONFIG_MESH */ + } else { + #ifdef CONFIG_MESH + if (hapd->conf->mesh & MESH_ENABLED) { +@@ -1073,7 +2250,7 @@ + /* if the mesh peer is not available, we don't do auth. + */ + wpa_printf(MSG_DEBUG, "Mesh peer " MACSTR +- " not yet known - drop Authentiation frame", ++ " not yet known - drop Authentication frame", + MAC2STR(mgmt->sa)); + /* + * Save a copy of the frame so that it can be processed +@@ -1088,6 +2265,7 @@ + + sta = ap_sta_add(hapd, mgmt->sa); + if (!sta) { ++ wpa_printf(MSG_DEBUG, "ap_sta_add() failed"); + resp = WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA; + goto fail; + } +@@ -1094,44 +2272,70 @@ + } + sta->last_seq_ctrl = seq_ctrl; + sta->last_subtype = WLAN_FC_STYPE_AUTH; ++#ifdef CONFIG_MBO ++ sta->auth_rssi = rssi; ++#endif /* CONFIG_MBO */ + +- if (vlan_id > 0) { +- if (!hostapd_vlan_id_valid(hapd->conf->vlan, vlan_id)) { +- hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_RADIUS, +- HOSTAPD_LEVEL_INFO, "Invalid VLAN ID " +- "%d received from RADIUS server", +- vlan_id); +- resp = WLAN_STATUS_UNSPECIFIED_FAILURE; +- goto fail; +- } +- sta->vlan_id = vlan_id; +- hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_RADIUS, +- HOSTAPD_LEVEL_INFO, "VLAN ID %d", sta->vlan_id); ++ res = ieee802_11_set_radius_info( ++ hapd, sta, res, session_timeout, acct_interim_interval, ++ &vlan_id, &psk, &identity, &radius_cui); ++ if (res) { ++ wpa_printf(MSG_DEBUG, "ieee802_11_set_radius_info() failed"); ++ resp = WLAN_STATUS_UNSPECIFIED_FAILURE; ++ goto fail; + } + +- hostapd_free_psk_list(sta->psk); +- if (hapd->conf->wpa_psk_radius != PSK_RADIUS_IGNORED) { +- sta->psk = psk; +- psk = NULL; +- } else { +- sta->psk = NULL; +- } +- +- sta->identity = identity; +- identity = NULL; +- sta->radius_cui = radius_cui; +- radius_cui = NULL; +- + sta->flags &= ~WLAN_STA_PREAUTH; + ieee802_1x_notify_pre_auth(sta->eapol_sm, 0); + +- if (hapd->conf->acct_interim_interval == 0 && acct_interim_interval) +- sta->acct_interim_interval = acct_interim_interval; +- if (res == HOSTAPD_ACL_ACCEPT_TIMEOUT) +- ap_sta_session_timeout(hapd, sta, session_timeout); +- else +- ap_sta_no_session_timeout(hapd, sta); ++ /* ++ * If the driver supports full AP client state, add a station to the ++ * driver before sending authentication reply to make sure the driver ++ * has resources, and not to go through the entire authentication and ++ * association handshake, and fail it at the end. ++ * ++ * If this is not the first transaction, in a multi-step authentication ++ * algorithm, the station already exists in the driver ++ * (sta->added_unassoc = 1) so skip it. ++ * ++ * In mesh mode, the station was already added to the driver when the ++ * NEW_PEER_CANDIDATE event is received. ++ * ++ * If PMF was negotiated for the existing association, skip this to ++ * avoid dropping the STA entry and the associated keys. This is needed ++ * to allow the original connection work until the attempt can complete ++ * (re)association, so that unprotected Authentication frame cannot be ++ * used to bypass PMF protection. ++ */ ++ if (FULL_AP_CLIENT_STATE_SUPP(hapd->iface->drv_flags) && ++ (!(sta->flags & WLAN_STA_MFP) || !ap_sta_is_authorized(sta)) && ++ !(hapd->conf->mesh & MESH_ENABLED) && ++ !(sta->added_unassoc)) { ++ /* ++ * If a station that is already associated to the AP, is trying ++ * to authenticate again, remove the STA entry, in order to make ++ * sure the STA PS state gets cleared and configuration gets ++ * updated. To handle this, station's added_unassoc flag is ++ * cleared once the station has completed association. ++ */ ++ ap_sta_set_authorized(hapd, sta, 0); ++ hostapd_drv_sta_remove(hapd, sta->addr); ++ sta->flags &= ~(WLAN_STA_ASSOC | WLAN_STA_AUTH | ++ WLAN_STA_AUTHORIZED); + ++ if (hostapd_sta_add(hapd, sta->addr, 0, 0, NULL, 0, 0, ++ NULL, NULL, sta->flags, 0, 0, 0, 0)) { ++ hostapd_logger(hapd, sta->addr, ++ HOSTAPD_MODULE_IEEE80211, ++ HOSTAPD_LEVEL_NOTICE, ++ "Could not add STA to kernel driver"); ++ resp = WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA; ++ goto fail; ++ } ++ ++ sta->added_unassoc = 1; ++ } ++ + switch (auth_alg) { + case WLAN_AUTH_OPEN: + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, +@@ -1146,6 +2350,9 @@ + case WLAN_AUTH_SHARED_KEY: + resp = auth_shared_key(hapd, sta, auth_transaction, challenge, + fc & WLAN_FC_ISWEP); ++ if (resp != 0) ++ wpa_printf(MSG_DEBUG, ++ "auth_shared_key() failed: status=%d", resp); + sta->auth_alg = WLAN_AUTH_SHARED_KEY; + mlme_authenticate_indication(hapd, sta); + if (sta->challenge && auth_transaction == 1) { +@@ -1157,7 +2364,7 @@ + } + break; + #endif /* CONFIG_NO_RC4 */ +-#ifdef CONFIG_IEEE80211R ++#ifdef CONFIG_IEEE80211R_AP + case WLAN_AUTH_FT: + sta->auth_alg = WLAN_AUTH_FT; + if (sta->wpa_sm == NULL) +@@ -1176,7 +2383,7 @@ + handle_auth_ft_finish, hapd); + /* handle_auth_ft_finish() callback will complete auth. */ + return; +-#endif /* CONFIG_IEEE80211R */ ++#endif /* CONFIG_IEEE80211R_AP */ + #ifdef CONFIG_SAE + case WLAN_AUTH_SAE: + #ifdef CONFIG_MESH +@@ -1198,6 +2405,15 @@ + status_code); + return; + #endif /* CONFIG_SAE */ ++#ifdef CONFIG_FILS ++ case WLAN_AUTH_FILS_SK: ++ case WLAN_AUTH_FILS_SK_PFS: ++ handle_auth_fils(hapd, sta, mgmt->u.auth.variable, ++ len - IEEE80211_HDRLEN - sizeof(mgmt->u.auth), ++ auth_alg, auth_transaction, status_code, ++ handle_auth_fils_finish); ++ return; ++#endif /* CONFIG_FILS */ + } + + fail: +@@ -1205,12 +2421,19 @@ + os_free(radius_cui); + hostapd_free_psk_list(psk); + +- send_auth_reply(hapd, mgmt->sa, mgmt->bssid, auth_alg, +- auth_transaction + 1, resp, resp_ies, resp_ies_len); ++ reply_res = send_auth_reply(hapd, mgmt->sa, mgmt->bssid, auth_alg, ++ auth_transaction + 1, resp, resp_ies, ++ resp_ies_len, "handle-auth"); ++ ++ if (sta && sta->added_unassoc && (resp != WLAN_STATUS_SUCCESS || ++ reply_res != WLAN_STATUS_SUCCESS)) { ++ hostapd_drv_sta_remove(hapd, sta->addr); ++ sta->added_unassoc = 0; ++ } + } + + +-static int hostapd_get_aid(struct hostapd_data *hapd, struct sta_info *sta) ++int hostapd_get_aid(struct hostapd_data *hapd, struct sta_info *sta) + { + int i, j = 32, aid; + +@@ -1220,6 +2443,9 @@ + return 0; + } + ++ if (TEST_FAIL()) ++ return -1; ++ + for (i = 0; i < AID_WORDS; i++) { + if (hapd->sta_aid[i] == (u32) -1) + continue; +@@ -1286,10 +2512,68 @@ + return WLAN_STATUS_SUCCESS; + } + ++static u16 check_multi_ap(struct hostapd_data *hapd, struct sta_info *sta, ++ const u8 *multi_ap_ie, size_t multi_ap_len) ++{ ++ u8 multi_ap_value = 0; + ++ sta->flags &= ~WLAN_STA_MULTI_AP; ++ ++ if (!hapd->conf->multi_ap) ++ return WLAN_STATUS_SUCCESS; ++ ++ if (multi_ap_ie) { ++ const u8 *multi_ap_subelem; ++ ++ multi_ap_subelem = get_ie(multi_ap_ie + 4, ++ multi_ap_len - 4, ++ MULTI_AP_SUB_ELEM_TYPE); ++ if (multi_ap_subelem && multi_ap_subelem[1] == 1) { ++ multi_ap_value = multi_ap_subelem[2]; ++ } else { ++ hostapd_logger(hapd, sta->addr, ++ HOSTAPD_MODULE_IEEE80211, ++ HOSTAPD_LEVEL_INFO, ++ "Multi-AP IE has missing or invalid Multi-AP subelement"); ++ return WLAN_STATUS_INVALID_IE; ++ } ++ } ++ ++ if (multi_ap_value && multi_ap_value != MULTI_AP_BACKHAUL_STA) ++ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, ++ HOSTAPD_LEVEL_INFO, ++ "Multi-AP IE with unexpected value 0x%02x", ++ multi_ap_value); ++ ++ if (!(multi_ap_value & MULTI_AP_BACKHAUL_STA)) { ++ if (hapd->conf->multi_ap & FRONTHAUL_BSS) ++ return WLAN_STATUS_SUCCESS; ++ ++ hostapd_logger(hapd, sta->addr, ++ HOSTAPD_MODULE_IEEE80211, ++ HOSTAPD_LEVEL_INFO, ++ "Non-Multi-AP STA tries to associate with backhaul-only BSS"); ++ return WLAN_STATUS_ASSOC_DENIED_UNSPEC; ++ } ++ ++ if (!(hapd->conf->multi_ap & BACKHAUL_BSS)) ++ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, ++ HOSTAPD_LEVEL_DEBUG, ++ "Backhaul STA tries to associate with fronthaul-only BSS"); ++ ++ sta->flags |= WLAN_STA_MULTI_AP; ++ return WLAN_STATUS_SUCCESS; ++} ++ ++ + static u16 copy_supp_rates(struct hostapd_data *hapd, struct sta_info *sta, + struct ieee802_11_elems *elems) + { ++ /* Supported rates not used in IEEE 802.11ad/DMG */ ++ if (hapd->iface->current_mode && ++ hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211AD) ++ return WLAN_STATUS_SUCCESS; ++ + if (!elems->supp_rates) { + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_DEBUG, +@@ -1327,10 +2611,188 @@ + } + #endif /* CONFIG_INTERWORKING */ + ++ if (ext_capab_ie_len > 0) { ++ sta->ecsa_supported = !!(ext_capab_ie[0] & BIT(2)); ++ os_free(sta->ext_capability); ++ sta->ext_capability = os_malloc(1 + ext_capab_ie_len); ++ if (sta->ext_capability) { ++ sta->ext_capability[0] = ext_capab_ie_len; ++ os_memcpy(sta->ext_capability + 1, ext_capab_ie, ++ ext_capab_ie_len); ++ } ++ } ++ + return WLAN_STATUS_SUCCESS; + } + + ++#ifdef CONFIG_OWE ++ ++static int owe_group_supported(struct hostapd_data *hapd, u16 group) ++{ ++ int i; ++ int *groups = hapd->conf->owe_groups; ++ ++ if (group != 19 && group != 20 && group != 21) ++ return 0; ++ ++ if (!groups) ++ return 1; ++ ++ for (i = 0; groups[i] > 0; i++) { ++ if (groups[i] == group) ++ return 1; ++ } ++ ++ return 0; ++} ++ ++ ++static u16 owe_process_assoc_req(struct hostapd_data *hapd, ++ struct sta_info *sta, const u8 *owe_dh, ++ u8 owe_dh_len) ++{ ++ struct wpabuf *secret, *pub, *hkey; ++ int res; ++ u8 prk[SHA512_MAC_LEN], pmkid[SHA512_MAC_LEN]; ++ const char *info = "OWE Key Generation"; ++ const u8 *addr[2]; ++ size_t len[2]; ++ u16 group; ++ size_t hash_len, prime_len; ++ ++ if (wpa_auth_sta_get_pmksa(sta->wpa_sm)) { ++ wpa_printf(MSG_DEBUG, "OWE: Using PMKSA caching"); ++ return WLAN_STATUS_SUCCESS; ++ } ++ ++ group = WPA_GET_LE16(owe_dh); ++ if (!owe_group_supported(hapd, group)) { ++ wpa_printf(MSG_DEBUG, "OWE: Unsupported DH group %u", group); ++ return WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED; ++ } ++ if (group == 19) ++ prime_len = 32; ++ else if (group == 20) ++ prime_len = 48; ++ else if (group == 21) ++ prime_len = 66; ++ else ++ return WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED; ++ ++ crypto_ecdh_deinit(sta->owe_ecdh); ++ sta->owe_ecdh = crypto_ecdh_init(group); ++ if (!sta->owe_ecdh) ++ return WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED; ++ sta->owe_group = group; ++ ++ secret = crypto_ecdh_set_peerkey(sta->owe_ecdh, 0, owe_dh + 2, ++ owe_dh_len - 2); ++ secret = wpabuf_zeropad(secret, prime_len); ++ if (!secret) { ++ wpa_printf(MSG_DEBUG, "OWE: Invalid peer DH public key"); ++ return WLAN_STATUS_UNSPECIFIED_FAILURE; ++ } ++ wpa_hexdump_buf_key(MSG_DEBUG, "OWE: DH shared secret", secret); ++ ++ /* prk = HKDF-extract(C | A | group, z) */ ++ ++ pub = crypto_ecdh_get_pubkey(sta->owe_ecdh, 0); ++ if (!pub) { ++ wpabuf_clear_free(secret); ++ return WLAN_STATUS_UNSPECIFIED_FAILURE; ++ } ++ ++ /* PMKID = Truncate-128(Hash(C | A)) */ ++ addr[0] = owe_dh + 2; ++ len[0] = owe_dh_len - 2; ++ addr[1] = wpabuf_head(pub); ++ len[1] = wpabuf_len(pub); ++ if (group == 19) { ++ res = sha256_vector(2, addr, len, pmkid); ++ hash_len = SHA256_MAC_LEN; ++ } else if (group == 20) { ++ res = sha384_vector(2, addr, len, pmkid); ++ hash_len = SHA384_MAC_LEN; ++ } else if (group == 21) { ++ res = sha512_vector(2, addr, len, pmkid); ++ hash_len = SHA512_MAC_LEN; ++ } else { ++ wpabuf_free(pub); ++ wpabuf_clear_free(secret); ++ return WLAN_STATUS_UNSPECIFIED_FAILURE; ++ } ++ pub = wpabuf_zeropad(pub, prime_len); ++ if (res < 0 || !pub) { ++ wpabuf_free(pub); ++ wpabuf_clear_free(secret); ++ return WLAN_STATUS_UNSPECIFIED_FAILURE; ++ } ++ ++ hkey = wpabuf_alloc(owe_dh_len - 2 + wpabuf_len(pub) + 2); ++ if (!hkey) { ++ wpabuf_free(pub); ++ wpabuf_clear_free(secret); ++ return WLAN_STATUS_UNSPECIFIED_FAILURE; ++ } ++ ++ wpabuf_put_data(hkey, owe_dh + 2, owe_dh_len - 2); /* C */ ++ wpabuf_put_buf(hkey, pub); /* A */ ++ wpabuf_free(pub); ++ wpabuf_put_le16(hkey, group); /* group */ ++ if (group == 19) ++ res = hmac_sha256(wpabuf_head(hkey), wpabuf_len(hkey), ++ wpabuf_head(secret), wpabuf_len(secret), prk); ++ else if (group == 20) ++ res = hmac_sha384(wpabuf_head(hkey), wpabuf_len(hkey), ++ wpabuf_head(secret), wpabuf_len(secret), prk); ++ else if (group == 21) ++ res = hmac_sha512(wpabuf_head(hkey), wpabuf_len(hkey), ++ wpabuf_head(secret), wpabuf_len(secret), prk); ++ wpabuf_clear_free(hkey); ++ wpabuf_clear_free(secret); ++ if (res < 0) ++ return WLAN_STATUS_UNSPECIFIED_FAILURE; ++ ++ wpa_hexdump_key(MSG_DEBUG, "OWE: prk", prk, hash_len); ++ ++ /* PMK = HKDF-expand(prk, "OWE Key Generation", n) */ ++ ++ os_free(sta->owe_pmk); ++ sta->owe_pmk = os_malloc(hash_len); ++ if (!sta->owe_pmk) { ++ os_memset(prk, 0, SHA512_MAC_LEN); ++ return WLAN_STATUS_UNSPECIFIED_FAILURE; ++ } ++ ++ if (group == 19) ++ res = hmac_sha256_kdf(prk, hash_len, NULL, (const u8 *) info, ++ os_strlen(info), sta->owe_pmk, hash_len); ++ else if (group == 20) ++ res = hmac_sha384_kdf(prk, hash_len, NULL, (const u8 *) info, ++ os_strlen(info), sta->owe_pmk, hash_len); ++ else if (group == 21) ++ res = hmac_sha512_kdf(prk, hash_len, NULL, (const u8 *) info, ++ os_strlen(info), sta->owe_pmk, hash_len); ++ os_memset(prk, 0, SHA512_MAC_LEN); ++ if (res < 0) { ++ os_free(sta->owe_pmk); ++ sta->owe_pmk = NULL; ++ return WLAN_STATUS_UNSPECIFIED_FAILURE; ++ } ++ sta->owe_pmk_len = hash_len; ++ ++ wpa_hexdump_key(MSG_DEBUG, "OWE: PMK", sta->owe_pmk, sta->owe_pmk_len); ++ wpa_hexdump(MSG_DEBUG, "OWE: PMKID", pmkid, PMKID_LEN); ++ wpa_auth_pmksa_add2(hapd->wpa_auth, sta->addr, sta->owe_pmk, ++ sta->owe_pmk_len, pmkid, 0, WPA_KEY_MGMT_OWE); ++ ++ return WLAN_STATUS_SUCCESS; ++} ++ ++#endif /* CONFIG_OWE */ ++ ++ + static u16 check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta, + const u8 *ies, size_t ies_len, int reassoc) + { +@@ -1359,6 +2821,11 @@ + resp = copy_supp_rates(hapd, sta, &elems); + if (resp != WLAN_STATUS_SUCCESS) + return resp; ++ ++ resp = check_multi_ap(hapd, sta, elems.multi_ap, elems.multi_ap_len); ++ if (resp != WLAN_STATUS_SUCCESS) ++ return resp; ++ + #ifdef CONFIG_IEEE80211N + resp = copy_sta_ht_capab(hapd, sta, elems.ht_capabilities); + if (resp != WLAN_STATUS_SUCCESS) +@@ -1378,6 +2845,10 @@ + if (resp != WLAN_STATUS_SUCCESS) + return resp; + ++ resp = copy_sta_vht_oper(hapd, sta, elems.vht_operation); ++ if (resp != WLAN_STATUS_SUCCESS) ++ return resp; ++ + resp = set_sta_vht_opmode(hapd, sta, elems.vht_opmode_notif); + if (resp != WLAN_STATUS_SUCCESS) + return resp; +@@ -1470,34 +2941,24 @@ + "state machine"); + return WLAN_STATUS_UNSPECIFIED_FAILURE; + } ++ wpa_auth_set_auth_alg(sta->wpa_sm, sta->auth_alg); + res = wpa_validate_wpa_ie(hapd->wpa_auth, sta->wpa_sm, ++ hapd->iface->freq, + wpa_ie, wpa_ie_len, +- elems.mdie, elems.mdie_len); +- if (res == WPA_INVALID_GROUP) +- resp = WLAN_STATUS_GROUP_CIPHER_NOT_VALID; +- else if (res == WPA_INVALID_PAIRWISE) +- resp = WLAN_STATUS_PAIRWISE_CIPHER_NOT_VALID; +- else if (res == WPA_INVALID_AKMP) +- resp = WLAN_STATUS_AKMP_NOT_VALID; +- else if (res == WPA_ALLOC_FAIL) +- resp = WLAN_STATUS_UNSPECIFIED_FAILURE; +-#ifdef CONFIG_IEEE80211W +- else if (res == WPA_MGMT_FRAME_PROTECTION_VIOLATION) +- resp = WLAN_STATUS_ROBUST_MGMT_FRAME_POLICY_VIOLATION; +- else if (res == WPA_INVALID_MGMT_GROUP_CIPHER) +- resp = WLAN_STATUS_ROBUST_MGMT_FRAME_POLICY_VIOLATION; +-#endif /* CONFIG_IEEE80211W */ +- else if (res == WPA_INVALID_MDIE) +- resp = WLAN_STATUS_INVALID_MDIE; +- else if (res != WPA_IE_OK) +- resp = WLAN_STATUS_INVALID_IE; ++ elems.mdie, elems.mdie_len, ++ elems.owe_dh, elems.owe_dh_len); ++ resp = wpa_res_to_status_code(res); + if (resp != WLAN_STATUS_SUCCESS) + return resp; + #ifdef CONFIG_IEEE80211W +- if ((sta->flags & WLAN_STA_MFP) && !sta->sa_query_timed_out && ++ if ((sta->flags & (WLAN_STA_ASSOC | WLAN_STA_MFP)) == ++ (WLAN_STA_ASSOC | WLAN_STA_MFP) && ++ !sta->sa_query_timed_out && + sta->sa_query_count > 0) + ap_check_sa_query_timeout(hapd, sta); +- if ((sta->flags & WLAN_STA_MFP) && !sta->sa_query_timed_out && ++ if ((sta->flags & (WLAN_STA_ASSOC | WLAN_STA_MFP)) == ++ (WLAN_STA_ASSOC | WLAN_STA_MFP) && ++ !sta->sa_query_timed_out && + (!reassoc || sta->auth_alg != WLAN_AUTH_FT)) { + /* + * STA has already been associated with MFP and SA +@@ -1518,7 +2979,7 @@ + sta->flags &= ~WLAN_STA_MFP; + #endif /* CONFIG_IEEE80211W */ + +-#ifdef CONFIG_IEEE80211R ++#ifdef CONFIG_IEEE80211R_AP + if (sta->auth_alg == WLAN_AUTH_FT) { + if (!reassoc) { + wpa_printf(MSG_DEBUG, "FT: " MACSTR " tried " +@@ -1533,9 +2994,13 @@ + if (resp != WLAN_STATUS_SUCCESS) + return resp; + } +-#endif /* CONFIG_IEEE80211R */ ++#endif /* CONFIG_IEEE80211R_AP */ + + #ifdef CONFIG_SAE ++ if (wpa_auth_uses_sae(sta->wpa_sm) && sta->sae && ++ sta->sae->state == SAE_ACCEPTED) ++ wpa_auth_add_sae_pmkid(sta->wpa_sm, sta->sae->pmkid); ++ + if (wpa_auth_uses_sae(sta->wpa_sm) && + sta->auth_alg == WLAN_AUTH_OPEN) { + struct rsn_pmksa_cache_entry *sa; +@@ -1559,6 +3024,48 @@ + } + #endif /* CONFIG_SAE */ + ++#ifdef CONFIG_OWE ++ if ((hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_OWE) && ++ wpa_auth_sta_key_mgmt(sta->wpa_sm) == WPA_KEY_MGMT_OWE && ++ elems.owe_dh) { ++ resp = owe_process_assoc_req(hapd, sta, elems.owe_dh, ++ elems.owe_dh_len); ++ if (resp != WLAN_STATUS_SUCCESS) ++ return resp; ++ } ++#endif /* CONFIG_OWE */ ++ ++#ifdef CONFIG_DPP2 ++ dpp_pfs_free(sta->dpp_pfs); ++ sta->dpp_pfs = NULL; ++ ++ if ((hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_DPP) && ++ hapd->conf->dpp_netaccesskey && sta->wpa_sm && ++ wpa_auth_sta_key_mgmt(sta->wpa_sm) == WPA_KEY_MGMT_DPP && ++ elems.owe_dh) { ++ sta->dpp_pfs = dpp_pfs_init( ++ wpabuf_head(hapd->conf->dpp_netaccesskey), ++ wpabuf_len(hapd->conf->dpp_netaccesskey)); ++ if (!sta->dpp_pfs) { ++ wpa_printf(MSG_DEBUG, ++ "DPP: Could not initialize PFS"); ++ /* Try to continue without PFS */ ++ goto pfs_fail; ++ } ++ ++ if (dpp_pfs_process(sta->dpp_pfs, elems.owe_dh, ++ elems.owe_dh_len) < 0) { ++ dpp_pfs_free(sta->dpp_pfs); ++ sta->dpp_pfs = NULL; ++ return WLAN_STATUS_UNSPECIFIED_FAILURE; ++ } ++ } ++ ++ wpa_auth_set_dpp_z(sta->wpa_sm, sta->dpp_pfs ? ++ sta->dpp_pfs->secret : NULL); ++ pfs_fail: ++#endif /* CONFIG_DPP2 */ ++ + #ifdef CONFIG_IEEE80211N + if ((sta->flags & (WLAN_STA_HT | WLAN_STA_VHT)) && + wpa_auth_get_pairwise(sta->wpa_sm) == WPA_CIPHER_TKIP) { +@@ -1603,10 +3110,28 @@ + #ifdef CONFIG_HS20 + wpabuf_free(sta->hs20_ie); + if (elems.hs20 && elems.hs20_len > 4) { ++ int release; ++ + sta->hs20_ie = wpabuf_alloc_copy(elems.hs20 + 4, + elems.hs20_len - 4); +- } else ++ release = ((elems.hs20[4] >> 4) & 0x0f) + 1; ++ if (release >= 2 && !wpa_auth_uses_mfp(sta->wpa_sm)) { ++ wpa_printf(MSG_DEBUG, ++ "HS 2.0: PMF not negotiated by release %d station " ++ MACSTR, release, MAC2STR(sta->addr)); ++ return WLAN_STATUS_ROBUST_MGMT_FRAME_POLICY_VIOLATION; ++ } ++ } else { + sta->hs20_ie = NULL; ++ } ++ ++ wpabuf_free(sta->roaming_consortium); ++ if (elems.roaming_cons_sel) ++ sta->roaming_consortium = wpabuf_alloc_copy( ++ elems.roaming_cons_sel + 4, ++ elems.roaming_cons_sel_len - 4); ++ else ++ sta->roaming_consortium = NULL; + #endif /* CONFIG_HS20 */ + + #ifdef CONFIG_FST +@@ -1617,6 +3142,64 @@ + sta->mb_ies = NULL; + #endif /* CONFIG_FST */ + ++#ifdef CONFIG_MBO ++ mbo_ap_check_sta_assoc(hapd, sta, &elems); ++ ++ if (hapd->conf->mbo_enabled && (hapd->conf->wpa & 2) && ++ elems.mbo && sta->cell_capa && !(sta->flags & WLAN_STA_MFP) && ++ hapd->conf->ieee80211w != NO_MGMT_FRAME_PROTECTION) { ++ wpa_printf(MSG_INFO, ++ "MBO: Reject WPA2 association without PMF"); ++ return WLAN_STATUS_UNSPECIFIED_FAILURE; ++ } ++#endif /* CONFIG_MBO */ ++ ++#if defined(CONFIG_FILS) && defined(CONFIG_OCV) ++ if (wpa_auth_uses_ocv(sta->wpa_sm) && ++ (sta->auth_alg == WLAN_AUTH_FILS_SK || ++ sta->auth_alg == WLAN_AUTH_FILS_SK_PFS || ++ sta->auth_alg == WLAN_AUTH_FILS_PK)) { ++ struct wpa_channel_info ci; ++ int tx_chanwidth; ++ int tx_seg1_idx; ++ ++ if (hostapd_drv_channel_info(hapd, &ci) != 0) { ++ wpa_printf(MSG_WARNING, ++ "Failed to get channel info to validate received OCI in FILS (Re)Association Request frame"); ++ return WLAN_STATUS_UNSPECIFIED_FAILURE; ++ } ++ ++ if (get_sta_tx_parameters(sta->wpa_sm, ++ channel_width_to_int(ci.chanwidth), ++ ci.seg1_idx, &tx_chanwidth, ++ &tx_seg1_idx) < 0) ++ return WLAN_STATUS_UNSPECIFIED_FAILURE; ++ ++ if (ocv_verify_tx_params(elems.oci, elems.oci_len, &ci, ++ tx_chanwidth, tx_seg1_idx) != 0) { ++ wpa_printf(MSG_WARNING, "FILS: %s", ocv_errorstr); ++ return WLAN_STATUS_UNSPECIFIED_FAILURE; ++ } ++ } ++#endif /* CONFIG_FILS && CONFIG_OCV */ ++ ++ ap_copy_sta_supp_op_classes(sta, elems.supp_op_classes, ++ elems.supp_op_classes_len); ++ ++ if ((sta->capability & WLAN_CAPABILITY_RADIO_MEASUREMENT) && ++ elems.rrm_enabled && ++ elems.rrm_enabled_len >= sizeof(sta->rrm_enabled_capa)) ++ os_memcpy(sta->rrm_enabled_capa, elems.rrm_enabled, ++ sizeof(sta->rrm_enabled_capa)); ++ ++ if (elems.power_capab) { ++ sta->min_tx_power = elems.power_capab[0]; ++ sta->max_tx_power = elems.power_capab[1]; ++ sta->power_capab = 1; ++ } else { ++ sta->power_capab = 0; ++ } ++ + return WLAN_STATUS_SUCCESS; + } + +@@ -1643,22 +3226,131 @@ + } + + +-static void send_assoc_resp(struct hostapd_data *hapd, struct sta_info *sta, +- u16 status_code, int reassoc, const u8 *ies, +- size_t ies_len) ++static int add_associated_sta(struct hostapd_data *hapd, ++ struct sta_info *sta, int reassoc) + { ++ struct ieee80211_ht_capabilities ht_cap; ++ struct ieee80211_vht_capabilities vht_cap; ++ int set = 1; ++ ++ /* ++ * Remove the STA entry to ensure the STA PS state gets cleared and ++ * configuration gets updated. This is relevant for cases, such as ++ * FT-over-the-DS, where a station re-associates back to the same AP but ++ * skips the authentication flow, or if working with a driver that ++ * does not support full AP client state. ++ * ++ * Skip this if the STA has already completed FT reassociation and the ++ * TK has been configured since the TX/RX PN must not be reset to 0 for ++ * the same key. ++ * ++ * FT-over-the-DS has a special case where the STA entry (and as such, ++ * the TK) has not yet been configured to the driver depending on which ++ * driver interface is used. For that case, allow add-STA operation to ++ * be used (instead of set-STA). This is needed to allow mac80211-based ++ * drivers to accept the STA parameter configuration. Since this is ++ * after a new FT-over-DS exchange, a new TK has been derived, so key ++ * reinstallation is not a concern for this case. ++ */ ++ wpa_printf(MSG_DEBUG, "Add associated STA " MACSTR ++ " (added_unassoc=%d auth_alg=%u ft_over_ds=%u reassoc=%d authorized=%d ft_tk=%d fils_tk=%d)", ++ MAC2STR(sta->addr), sta->added_unassoc, sta->auth_alg, ++ sta->ft_over_ds, reassoc, ++ !!(sta->flags & WLAN_STA_AUTHORIZED), ++ wpa_auth_sta_ft_tk_already_set(sta->wpa_sm), ++ wpa_auth_sta_fils_tk_already_set(sta->wpa_sm)); ++ ++ if (!sta->added_unassoc && ++ (!(sta->flags & WLAN_STA_AUTHORIZED) || ++ (reassoc && sta->ft_over_ds && sta->auth_alg == WLAN_AUTH_FT) || ++ (!wpa_auth_sta_ft_tk_already_set(sta->wpa_sm) && ++ !wpa_auth_sta_fils_tk_already_set(sta->wpa_sm)))) { ++ hostapd_drv_sta_remove(hapd, sta->addr); ++ wpa_auth_sm_event(sta->wpa_sm, WPA_DRV_STA_REMOVED); ++ set = 0; ++ ++ /* Do not allow the FT-over-DS exception to be used more than ++ * once per authentication exchange to guarantee a new TK is ++ * used here */ ++ sta->ft_over_ds = 0; ++ } ++ ++#ifdef CONFIG_IEEE80211N ++ if (sta->flags & WLAN_STA_HT) ++ hostapd_get_ht_capab(hapd, sta->ht_capabilities, &ht_cap); ++#endif /* CONFIG_IEEE80211N */ ++#ifdef CONFIG_IEEE80211AC ++ if (sta->flags & WLAN_STA_VHT) ++ hostapd_get_vht_capab(hapd, sta->vht_capabilities, &vht_cap); ++#endif /* CONFIG_IEEE80211AC */ ++ ++ /* ++ * Add the station with forced WLAN_STA_ASSOC flag. The sta->flags ++ * will be set when the ACK frame for the (Re)Association Response frame ++ * is processed (TX status driver event). ++ */ ++ if (hostapd_sta_add(hapd, sta->addr, sta->aid, sta->capability, ++ sta->supported_rates, sta->supported_rates_len, ++ sta->listen_interval, ++ sta->flags & WLAN_STA_HT ? &ht_cap : NULL, ++ sta->flags & WLAN_STA_VHT ? &vht_cap : NULL, ++ sta->flags | WLAN_STA_ASSOC, sta->qosinfo, ++ sta->vht_opmode, sta->p2p_ie ? 1 : 0, ++ set)) { ++ hostapd_logger(hapd, sta->addr, ++ HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_NOTICE, ++ "Could not %s STA to kernel driver", ++ set ? "set" : "add"); ++ ++ if (sta->added_unassoc) { ++ hostapd_drv_sta_remove(hapd, sta->addr); ++ sta->added_unassoc = 0; ++ } ++ ++ return -1; ++ } ++ ++ sta->added_unassoc = 0; ++ ++ return 0; ++} ++ ++ ++static u16 send_assoc_resp(struct hostapd_data *hapd, struct sta_info *sta, ++ const u8 *addr, u16 status_code, int reassoc, ++ const u8 *ies, size_t ies_len, int rssi) ++{ + int send_len; +- u8 buf[sizeof(struct ieee80211_mgmt) + 1024]; ++ u8 *buf; ++ size_t buflen; + struct ieee80211_mgmt *reply; + u8 *p; ++ u16 res = WLAN_STATUS_SUCCESS; + +- os_memset(buf, 0, sizeof(buf)); ++ buflen = sizeof(struct ieee80211_mgmt) + 1024; ++#ifdef CONFIG_FILS ++ if (sta && sta->fils_hlp_resp) ++ buflen += wpabuf_len(sta->fils_hlp_resp); ++#endif /* CONFIG_FILS */ ++#ifdef CONFIG_OWE ++ if (sta && (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_OWE)) ++ buflen += 150; ++#endif /* CONFIG_OWE */ ++#ifdef CONFIG_DPP2 ++ if (sta && sta->dpp_pfs) ++ buflen += 5 + sta->dpp_pfs->curve->prime_len; ++#endif /* CONFIG_DPP2 */ ++ buf = os_zalloc(buflen); ++ if (!buf) { ++ res = WLAN_STATUS_UNSPECIFIED_FAILURE; ++ goto done; ++ } + reply = (struct ieee80211_mgmt *) buf; + reply->frame_control = + IEEE80211_FC(WLAN_FC_TYPE_MGMT, + (reassoc ? WLAN_FC_STYPE_REASSOC_RESP : + WLAN_FC_STYPE_ASSOC_RESP)); +- os_memcpy(reply->da, sta->addr, ETH_ALEN); ++ os_memcpy(reply->da, addr, ETH_ALEN); + os_memcpy(reply->sa, hapd->own_addr, ETH_ALEN); + os_memcpy(reply->bssid, hapd->own_addr, ETH_ALEN); + +@@ -1667,24 +3359,50 @@ + reply->u.assoc_resp.capab_info = + host_to_le16(hostapd_own_capab_info(hapd)); + reply->u.assoc_resp.status_code = host_to_le16(status_code); +- reply->u.assoc_resp.aid = host_to_le16(sta->aid | BIT(14) | BIT(15)); ++ ++ reply->u.assoc_resp.aid = host_to_le16((sta ? sta->aid : 0) | ++ BIT(14) | BIT(15)); + /* Supported rates */ + p = hostapd_eid_supp_rates(hapd, reply->u.assoc_resp.variable); + /* Extended supported rates */ + p = hostapd_eid_ext_supp_rates(hapd, p); + +-#ifdef CONFIG_IEEE80211R +- if (status_code == WLAN_STATUS_SUCCESS) { ++#ifdef CONFIG_MBO ++ if (status_code == WLAN_STATUS_DENIED_POOR_CHANNEL_CONDITIONS && ++ rssi != 0) { ++ int delta = hapd->iconf->rssi_reject_assoc_rssi - rssi; ++ ++ p = hostapd_eid_mbo_rssi_assoc_rej(hapd, p, buf + buflen - p, ++ delta); ++ } ++#endif /* CONFIG_MBO */ ++ ++#ifdef CONFIG_IEEE80211R_AP ++ if (sta && status_code == WLAN_STATUS_SUCCESS) { + /* IEEE 802.11r: Mobility Domain Information, Fast BSS + * Transition Information, RSN, [RIC Response] */ + p = wpa_sm_write_assoc_resp_ies(sta->wpa_sm, p, +- buf + sizeof(buf) - p, ++ buf + buflen - p, + sta->auth_alg, ies, ies_len); ++ if (!p) { ++ wpa_printf(MSG_DEBUG, ++ "FT: Failed to write AssocResp IEs"); ++ res = WLAN_STATUS_UNSPECIFIED_FAILURE; ++ goto done; ++ } + } +-#endif /* CONFIG_IEEE80211R */ ++#endif /* CONFIG_IEEE80211R_AP */ + ++#ifdef CONFIG_OWE ++ if (sta && status_code == WLAN_STATUS_SUCCESS && ++ (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_OWE)) ++ p = wpa_auth_write_assoc_resp_owe(sta->wpa_sm, p, ++ buf + buflen - p, ++ ies, ies_len); ++#endif /* CONFIG_OWE */ ++ + #ifdef CONFIG_IEEE80211W +- if (status_code == WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY) ++ if (sta && status_code == WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY) + p = hostapd_eid_assoc_comeback_time(hapd, sta, p); + #endif /* CONFIG_IEEE80211W */ + +@@ -1695,7 +3413,23 @@ + + #ifdef CONFIG_IEEE80211AC + if (hapd->iconf->ieee80211ac && !hapd->conf->disable_11ac) { +- p = hostapd_eid_vht_capabilities(hapd, p); ++ u32 nsts = 0, sta_nsts; ++ ++ if (sta && hapd->conf->use_sta_nsts && sta->vht_capabilities) { ++ struct ieee80211_vht_capabilities *capa; ++ ++ nsts = (hapd->iface->conf->vht_capab >> ++ VHT_CAP_BEAMFORMEE_STS_OFFSET) & 7; ++ capa = sta->vht_capabilities; ++ sta_nsts = (le_to_host32(capa->vht_capabilities_info) >> ++ VHT_CAP_BEAMFORMEE_STS_OFFSET) & 7; ++ ++ if (nsts < sta_nsts) ++ nsts = 0; ++ else ++ nsts = sta_nsts; ++ } ++ p = hostapd_eid_vht_capabilities(hapd, p, nsts); + p = hostapd_eid_vht_operation(hapd, p); + } + #endif /* CONFIG_IEEE80211AC */ +@@ -1702,7 +3436,7 @@ + + p = hostapd_eid_ext_capab(hapd, p); + p = hostapd_eid_bss_max_idle_period(hapd, p); +- if (sta->qos_map_enabled) ++ if (sta && sta->qos_map_enabled) + p = hostapd_eid_qos_map_set(hapd, p); + + #ifdef CONFIG_FST +@@ -1713,17 +3447,51 @@ + } + #endif /* CONFIG_FST */ + ++#ifdef CONFIG_OWE ++ if ((hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_OWE) && ++ sta && sta->owe_ecdh && status_code == WLAN_STATUS_SUCCESS && ++ wpa_auth_sta_key_mgmt(sta->wpa_sm) == WPA_KEY_MGMT_OWE) { ++ struct wpabuf *pub; ++ ++ pub = crypto_ecdh_get_pubkey(sta->owe_ecdh, 0); ++ if (!pub) { ++ res = WLAN_STATUS_UNSPECIFIED_FAILURE; ++ goto done; ++ } ++ /* OWE Diffie-Hellman Parameter element */ ++ *p++ = WLAN_EID_EXTENSION; /* Element ID */ ++ *p++ = 1 + 2 + wpabuf_len(pub); /* Length */ ++ *p++ = WLAN_EID_EXT_OWE_DH_PARAM; /* Element ID Extension */ ++ WPA_PUT_LE16(p, sta->owe_group); ++ p += 2; ++ os_memcpy(p, wpabuf_head(pub), wpabuf_len(pub)); ++ p += wpabuf_len(pub); ++ wpabuf_free(pub); ++ } ++#endif /* CONFIG_OWE */ ++ ++#ifdef CONFIG_DPP2 ++ if ((hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_DPP) && ++ sta && sta->dpp_pfs && status_code == WLAN_STATUS_SUCCESS && ++ wpa_auth_sta_key_mgmt(sta->wpa_sm) == WPA_KEY_MGMT_DPP) { ++ os_memcpy(p, wpabuf_head(sta->dpp_pfs->ie), ++ wpabuf_len(sta->dpp_pfs->ie)); ++ p += wpabuf_len(sta->dpp_pfs->ie); ++ } ++#endif /* CONFIG_DPP2 */ ++ + #ifdef CONFIG_IEEE80211AC +- if (hapd->conf->vendor_vht && (sta->flags & WLAN_STA_VENDOR_VHT)) ++ if (sta && hapd->conf->vendor_vht && (sta->flags & WLAN_STA_VENDOR_VHT)) + p = hostapd_eid_vendor_vht(hapd, p); + #endif /* CONFIG_IEEE80211AC */ + +- if (sta->flags & WLAN_STA_WMM) ++ if (sta && (sta->flags & WLAN_STA_WMM)) + p = hostapd_eid_wmm(hapd, p); + + #ifdef CONFIG_WPS +- if ((sta->flags & WLAN_STA_WPS) || +- ((sta->flags & WLAN_STA_MAYBE_WPS) && hapd->conf->wpa)) { ++ if (sta && ++ ((sta->flags & WLAN_STA_WPS) || ++ ((sta->flags & WLAN_STA_MAYBE_WPS) && hapd->conf->wpa))) { + struct wpabuf *wps = wps_build_assoc_resp_ie(); + if (wps) { + os_memcpy(p, wpabuf_head(wps), wpabuf_len(wps)); +@@ -1733,8 +3501,11 @@ + } + #endif /* CONFIG_WPS */ + ++ if (sta && (sta->flags & WLAN_STA_MULTI_AP)) ++ p = hostapd_eid_multi_ap(hapd, p); ++ + #ifdef CONFIG_P2P +- if (sta->p2p_ie) { ++ if (sta && sta->p2p_ie && hapd->p2p_group) { + struct wpabuf *p2p_resp_ie; + enum p2p_status_code status; + switch (status_code) { +@@ -1763,23 +3534,180 @@ + p = hostapd_eid_p2p_manage(hapd, p); + #endif /* CONFIG_P2P_MANAGER */ + ++ p = hostapd_eid_mbo(hapd, p, buf + buflen - p); ++ ++ if (hapd->conf->assocresp_elements && ++ (size_t) (buf + buflen - p) >= ++ wpabuf_len(hapd->conf->assocresp_elements)) { ++ os_memcpy(p, wpabuf_head(hapd->conf->assocresp_elements), ++ wpabuf_len(hapd->conf->assocresp_elements)); ++ p += wpabuf_len(hapd->conf->assocresp_elements); ++ } ++ + send_len += p - reply->u.assoc_resp.variable; + +- if (hostapd_drv_send_mlme(hapd, reply, send_len, 0) < 0) ++#ifdef CONFIG_FILS ++ if (sta && ++ (sta->auth_alg == WLAN_AUTH_FILS_SK || ++ sta->auth_alg == WLAN_AUTH_FILS_SK_PFS || ++ sta->auth_alg == WLAN_AUTH_FILS_PK) && ++ status_code == WLAN_STATUS_SUCCESS) { ++ struct ieee802_11_elems elems; ++ ++ if (ieee802_11_parse_elems(ies, ies_len, &elems, 0) == ++ ParseFailed || !elems.fils_session) { ++ res = WLAN_STATUS_UNSPECIFIED_FAILURE; ++ goto done; ++ } ++ ++ /* FILS Session */ ++ *p++ = WLAN_EID_EXTENSION; /* Element ID */ ++ *p++ = 1 + FILS_SESSION_LEN; /* Length */ ++ *p++ = WLAN_EID_EXT_FILS_SESSION; /* Element ID Extension */ ++ os_memcpy(p, elems.fils_session, FILS_SESSION_LEN); ++ send_len += 2 + 1 + FILS_SESSION_LEN; ++ ++ send_len = fils_encrypt_assoc(sta->wpa_sm, buf, send_len, ++ buflen, sta->fils_hlp_resp); ++ if (send_len < 0) { ++ res = WLAN_STATUS_UNSPECIFIED_FAILURE; ++ goto done; ++ } ++ } ++#endif /* CONFIG_FILS */ ++ ++ if (hostapd_drv_send_mlme(hapd, reply, send_len, 0) < 0) { + wpa_printf(MSG_INFO, "Failed to send assoc resp: %s", + strerror(errno)); ++ res = WLAN_STATUS_UNSPECIFIED_FAILURE; ++ } ++ ++done: ++ os_free(buf); ++ return res; + } + + ++#ifdef CONFIG_OWE ++u8 * owe_assoc_req_process(struct hostapd_data *hapd, struct sta_info *sta, ++ const u8 *owe_dh, u8 owe_dh_len, ++ u8 *owe_buf, size_t owe_buf_len, u16 *reason) ++{ ++#ifdef CONFIG_TESTING_OPTIONS ++ if (hapd->conf->own_ie_override) { ++ wpa_printf(MSG_DEBUG, "OWE: Using IE override"); ++ *reason = WLAN_STATUS_SUCCESS; ++ return wpa_auth_write_assoc_resp_owe(sta->wpa_sm, owe_buf, ++ owe_buf_len, NULL, 0); ++ } ++#endif /* CONFIG_TESTING_OPTIONS */ ++ ++ if (wpa_auth_sta_get_pmksa(sta->wpa_sm)) { ++ wpa_printf(MSG_DEBUG, "OWE: Using PMKSA caching"); ++ owe_buf = wpa_auth_write_assoc_resp_owe(sta->wpa_sm, owe_buf, ++ owe_buf_len, NULL, 0); ++ *reason = WLAN_STATUS_SUCCESS; ++ return owe_buf; ++ } ++ ++ *reason = owe_process_assoc_req(hapd, sta, owe_dh, owe_dh_len); ++ if (*reason != WLAN_STATUS_SUCCESS) ++ return NULL; ++ ++ owe_buf = wpa_auth_write_assoc_resp_owe(sta->wpa_sm, owe_buf, ++ owe_buf_len, NULL, 0); ++ ++ if (sta->owe_ecdh && owe_buf) { ++ struct wpabuf *pub; ++ ++ pub = crypto_ecdh_get_pubkey(sta->owe_ecdh, 0); ++ if (!pub) { ++ *reason = WLAN_STATUS_UNSPECIFIED_FAILURE; ++ return owe_buf; ++ } ++ ++ /* OWE Diffie-Hellman Parameter element */ ++ *owe_buf++ = WLAN_EID_EXTENSION; /* Element ID */ ++ *owe_buf++ = 1 + 2 + wpabuf_len(pub); /* Length */ ++ *owe_buf++ = WLAN_EID_EXT_OWE_DH_PARAM; /* Element ID Extension ++ */ ++ WPA_PUT_LE16(owe_buf, sta->owe_group); ++ owe_buf += 2; ++ os_memcpy(owe_buf, wpabuf_head(pub), wpabuf_len(pub)); ++ owe_buf += wpabuf_len(pub); ++ wpabuf_free(pub); ++ } ++ ++ return owe_buf; ++} ++#endif /* CONFIG_OWE */ ++ ++ ++#ifdef CONFIG_FILS ++ ++void fils_hlp_finish_assoc(struct hostapd_data *hapd, struct sta_info *sta) ++{ ++ u16 reply_res; ++ ++ wpa_printf(MSG_DEBUG, "FILS: Finish association with " MACSTR, ++ MAC2STR(sta->addr)); ++ eloop_cancel_timeout(fils_hlp_timeout, hapd, sta); ++ if (!sta->fils_pending_assoc_req) ++ return; ++ reply_res = send_assoc_resp(hapd, sta, sta->addr, WLAN_STATUS_SUCCESS, ++ sta->fils_pending_assoc_is_reassoc, ++ sta->fils_pending_assoc_req, ++ sta->fils_pending_assoc_req_len, 0); ++ os_free(sta->fils_pending_assoc_req); ++ sta->fils_pending_assoc_req = NULL; ++ sta->fils_pending_assoc_req_len = 0; ++ wpabuf_free(sta->fils_hlp_resp); ++ sta->fils_hlp_resp = NULL; ++ wpabuf_free(sta->hlp_dhcp_discover); ++ sta->hlp_dhcp_discover = NULL; ++ ++ /* ++ * Remove the station in case transmission of a success response fails. ++ * At this point the station was already added associated to the driver. ++ */ ++ if (reply_res != WLAN_STATUS_SUCCESS) ++ hostapd_drv_sta_remove(hapd, sta->addr); ++} ++ ++ ++void fils_hlp_timeout(void *eloop_ctx, void *eloop_data) ++{ ++ struct hostapd_data *hapd = eloop_ctx; ++ struct sta_info *sta = eloop_data; ++ ++ wpa_printf(MSG_DEBUG, ++ "FILS: HLP response timeout - continue with association response for " ++ MACSTR, MAC2STR(sta->addr)); ++ if (sta->fils_drv_assoc_finish) ++ hostapd_notify_assoc_fils_finish(hapd, sta); ++ else ++ fils_hlp_finish_assoc(hapd, sta); ++} ++ ++#endif /* CONFIG_FILS */ ++ ++ + static void handle_assoc(struct hostapd_data *hapd, + const struct ieee80211_mgmt *mgmt, size_t len, +- int reassoc) ++ int reassoc, int rssi) + { + u16 capab_info, listen_interval, seq_ctrl, fc; +- u16 resp = WLAN_STATUS_SUCCESS; ++ u16 resp = WLAN_STATUS_SUCCESS, reply_res; + const u8 *pos; + int left, i; + struct sta_info *sta; ++ u8 *tmp = NULL; ++ struct hostapd_sta_wpa_psk_short *psk = NULL; ++ char *identity = NULL; ++ char *radius_cui = NULL; ++#ifdef CONFIG_FILS ++ int delay_assoc = 0; ++#endif /* CONFIG_FILS */ + + if (len < IEEE80211_HDRLEN + (reassoc ? sizeof(mgmt->u.reassoc_req) : + sizeof(mgmt->u.assoc_req))) { +@@ -1837,31 +3765,89 @@ + } + + sta = ap_get_sta(hapd, mgmt->sa); +-#ifdef CONFIG_IEEE80211R ++#ifdef CONFIG_IEEE80211R_AP + if (sta && sta->auth_alg == WLAN_AUTH_FT && + (sta->flags & WLAN_STA_AUTH) == 0) { + wpa_printf(MSG_DEBUG, "FT: Allow STA " MACSTR " to associate " + "prior to authentication since it is using " + "over-the-DS FT", MAC2STR(mgmt->sa)); ++ ++ /* ++ * Mark station as authenticated, to avoid adding station ++ * entry in the driver as associated and not authenticated ++ */ ++ sta->flags |= WLAN_STA_AUTH; + } else +-#endif /* CONFIG_IEEE80211R */ ++#endif /* CONFIG_IEEE80211R_AP */ + if (sta == NULL || (sta->flags & WLAN_STA_AUTH) == 0) { +- hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211, +- HOSTAPD_LEVEL_INFO, "Station tried to " +- "associate before authentication " +- "(aid=%d flags=0x%x)", +- sta ? sta->aid : -1, +- sta ? sta->flags : 0); +- send_deauth(hapd, mgmt->sa, +- WLAN_REASON_CLASS2_FRAME_FROM_NONAUTH_STA); +- return; ++ if (hapd->iface->current_mode && ++ hapd->iface->current_mode->mode == ++ HOSTAPD_MODE_IEEE80211AD) { ++ int acl_res; ++ u32 session_timeout, acct_interim_interval; ++ struct vlan_description vlan_id; ++ ++ acl_res = ieee802_11_allowed_address( ++ hapd, mgmt->sa, (const u8 *) mgmt, len, ++ &session_timeout, &acct_interim_interval, ++ &vlan_id, &psk, &identity, &radius_cui, 0); ++ if (acl_res == HOSTAPD_ACL_REJECT) { ++ wpa_msg(hapd->msg_ctx, MSG_DEBUG, ++ "Ignore Association Request frame from " ++ MACSTR " due to ACL reject", ++ MAC2STR(mgmt->sa)); ++ resp = WLAN_STATUS_UNSPECIFIED_FAILURE; ++ goto fail; ++ } ++ if (acl_res == HOSTAPD_ACL_PENDING) ++ return; ++ ++ /* DMG/IEEE 802.11ad does not use authentication. ++ * Allocate sta entry upon association. */ ++ sta = ap_sta_add(hapd, mgmt->sa); ++ if (!sta) { ++ hostapd_logger(hapd, mgmt->sa, ++ HOSTAPD_MODULE_IEEE80211, ++ HOSTAPD_LEVEL_INFO, ++ "Failed to add STA"); ++ resp = WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA; ++ goto fail; ++ } ++ ++ acl_res = ieee802_11_set_radius_info( ++ hapd, sta, acl_res, session_timeout, ++ acct_interim_interval, &vlan_id, &psk, ++ &identity, &radius_cui); ++ if (acl_res) { ++ resp = WLAN_STATUS_UNSPECIFIED_FAILURE; ++ goto fail; ++ } ++ ++ hostapd_logger(hapd, sta->addr, ++ HOSTAPD_MODULE_IEEE80211, ++ HOSTAPD_LEVEL_DEBUG, ++ "Skip authentication for DMG/IEEE 802.11ad"); ++ sta->flags |= WLAN_STA_AUTH; ++ wpa_auth_sm_event(sta->wpa_sm, WPA_AUTH); ++ sta->auth_alg = WLAN_AUTH_OPEN; ++ } else { ++ hostapd_logger(hapd, mgmt->sa, ++ HOSTAPD_MODULE_IEEE80211, ++ HOSTAPD_LEVEL_INFO, ++ "Station tried to associate before authentication (aid=%d flags=0x%x)", ++ sta ? sta->aid : -1, ++ sta ? sta->flags : 0); ++ send_deauth(hapd, mgmt->sa, ++ WLAN_REASON_CLASS2_FRAME_FROM_NONAUTH_STA); ++ return; ++ } + } + + if ((fc & WLAN_FC_RETRY) && + sta->last_seq_ctrl != WLAN_INVALID_MGMT_SEQ && + sta->last_seq_ctrl == seq_ctrl && +- sta->last_subtype == reassoc ? WLAN_FC_STYPE_REASSOC_REQ : +- WLAN_FC_STYPE_ASSOC_REQ) { ++ sta->last_subtype == (reassoc ? WLAN_FC_STYPE_REASSOC_REQ : ++ WLAN_FC_STYPE_ASSOC_REQ)) { + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_DEBUG, + "Drop repeated association frame seq_ctrl=0x%x", +@@ -1873,7 +3859,7 @@ + WLAN_FC_STYPE_ASSOC_REQ; + + if (hapd->tkip_countermeasures) { +- resp = WLAN_REASON_MICHAEL_MIC_FAILURE; ++ resp = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto fail; + } + +@@ -1886,6 +3872,53 @@ + goto fail; + } + ++#ifdef CONFIG_MBO ++ if (hapd->conf->mbo_enabled && hapd->mbo_assoc_disallow) { ++ resp = WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA; ++ goto fail; ++ } ++ ++ if (hapd->iconf->rssi_reject_assoc_rssi && rssi && ++ rssi < hapd->iconf->rssi_reject_assoc_rssi && ++ (sta->auth_rssi == 0 || ++ sta->auth_rssi < hapd->iconf->rssi_reject_assoc_rssi)) { ++ resp = WLAN_STATUS_DENIED_POOR_CHANNEL_CONDITIONS; ++ goto fail; ++ } ++#endif /* CONFIG_MBO */ ++ ++ /* ++ * sta->capability is used in check_assoc_ies() for RRM enabled ++ * capability element. ++ */ ++ sta->capability = capab_info; ++ ++#ifdef CONFIG_FILS ++ if (sta->auth_alg == WLAN_AUTH_FILS_SK || ++ sta->auth_alg == WLAN_AUTH_FILS_SK_PFS || ++ sta->auth_alg == WLAN_AUTH_FILS_PK) { ++ int res; ++ ++ /* The end of the payload is encrypted. Need to decrypt it ++ * before parsing. */ ++ ++ tmp = os_memdup(pos, left); ++ if (!tmp) { ++ resp = WLAN_STATUS_UNSPECIFIED_FAILURE; ++ goto fail; ++ } ++ ++ res = fils_decrypt_assoc(sta->wpa_sm, sta->fils_session, mgmt, ++ len, tmp, left); ++ if (res < 0) { ++ resp = WLAN_STATUS_UNSPECIFIED_FAILURE; ++ goto fail; ++ } ++ pos = tmp; ++ left = res; ++ } ++#endif /* CONFIG_FILS */ ++ + /* followed by SSID and Supported rates; and HT capabilities if 802.11n + * is used */ + resp = check_assoc_ies(hapd, sta, pos, left, reassoc); +@@ -1899,10 +3932,10 @@ + goto fail; + } + +- sta->capability = capab_info; + sta->listen_interval = listen_interval; + +- if (hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G) ++ if (hapd->iface->current_mode && ++ hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G) + sta->flags |= WLAN_STA_NONERP; + for (i = 0; i < sta->supported_rates_len; i++) { + if ((sta->supported_rates[i] & 0x7f) > 22) { +@@ -1921,7 +3954,8 @@ + !sta->no_short_slot_time_set) { + sta->no_short_slot_time_set = 1; + hapd->iface->num_sta_no_short_slot_time++; +- if (hapd->iface->current_mode->mode == ++ if (hapd->iface->current_mode && ++ hapd->iface->current_mode->mode == + HOSTAPD_MODE_IEEE80211G && + hapd->iface->num_sta_no_short_slot_time == 1) + ieee802_11_set_beacons(hapd->iface); +@@ -1936,7 +3970,8 @@ + !sta->no_short_preamble_set) { + sta->no_short_preamble_set = 1; + hapd->iface->num_sta_no_short_preamble++; +- if (hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G ++ if (hapd->iface->current_mode && ++ hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G + && hapd->iface->num_sta_no_short_preamble == 1) + ieee802_11_set_beacons(hapd->iface); + } +@@ -1969,8 +4004,99 @@ + * remove the STA immediately. */ + sta->timeout_next = STA_NULLFUNC; + ++#ifdef CONFIG_TAXONOMY ++ taxonomy_sta_info_assoc_req(hapd, sta, pos, left); ++#endif /* CONFIG_TAXONOMY */ ++ ++ sta->pending_wds_enable = 0; ++ ++#ifdef CONFIG_FILS ++ if (sta->auth_alg == WLAN_AUTH_FILS_SK || ++ sta->auth_alg == WLAN_AUTH_FILS_SK_PFS || ++ sta->auth_alg == WLAN_AUTH_FILS_PK) { ++ if (fils_process_hlp(hapd, sta, pos, left) > 0) ++ delay_assoc = 1; ++ } ++#endif /* CONFIG_FILS */ ++ + fail: +- send_assoc_resp(hapd, sta, resp, reassoc, pos, left); ++ os_free(identity); ++ os_free(radius_cui); ++ hostapd_free_psk_list(psk); ++ ++ /* ++ * In case of a successful response, add the station to the driver. ++ * Otherwise, the kernel may ignore Data frames before we process the ++ * ACK frame (TX status). In case of a failure, this station will be ++ * removed. ++ * ++ * Note that this is not compliant with the IEEE 802.11 standard that ++ * states that a non-AP station should transition into the ++ * authenticated/associated state only after the station acknowledges ++ * the (Re)Association Response frame. However, still do this as: ++ * ++ * 1. In case the station does not acknowledge the (Re)Association ++ * Response frame, it will be removed. ++ * 2. Data frames will be dropped in the kernel until the station is ++ * set into authorized state, and there are no significant known ++ * issues with processing other non-Data Class 3 frames during this ++ * window. ++ */ ++ if (resp == WLAN_STATUS_SUCCESS && sta && ++ add_associated_sta(hapd, sta, reassoc)) ++ resp = WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA; ++ ++#ifdef CONFIG_FILS ++ if (sta && delay_assoc && resp == WLAN_STATUS_SUCCESS && ++ eloop_is_timeout_registered(fils_hlp_timeout, hapd, sta) && ++ sta->fils_pending_assoc_req) { ++ /* Do not reschedule fils_hlp_timeout in case the station ++ * retransmits (Re)Association Request frame while waiting for ++ * the previously started FILS HLP wait, so that the timeout can ++ * be determined from the first pending attempt. */ ++ wpa_printf(MSG_DEBUG, ++ "FILS: Continue waiting for HLP processing before sending (Re)Association Response frame to " ++ MACSTR, MAC2STR(sta->addr)); ++ os_free(tmp); ++ return; ++ } ++ if (sta) { ++ eloop_cancel_timeout(fils_hlp_timeout, hapd, sta); ++ os_free(sta->fils_pending_assoc_req); ++ sta->fils_pending_assoc_req = NULL; ++ sta->fils_pending_assoc_req_len = 0; ++ wpabuf_free(sta->fils_hlp_resp); ++ sta->fils_hlp_resp = NULL; ++ } ++ if (sta && delay_assoc && resp == WLAN_STATUS_SUCCESS) { ++ sta->fils_pending_assoc_req = tmp; ++ sta->fils_pending_assoc_req_len = left; ++ sta->fils_pending_assoc_is_reassoc = reassoc; ++ sta->fils_drv_assoc_finish = 0; ++ wpa_printf(MSG_DEBUG, ++ "FILS: Waiting for HLP processing before sending (Re)Association Response frame to " ++ MACSTR, MAC2STR(sta->addr)); ++ eloop_cancel_timeout(fils_hlp_timeout, hapd, sta); ++ eloop_register_timeout(0, hapd->conf->fils_hlp_wait_time * 1024, ++ fils_hlp_timeout, hapd, sta); ++ return; ++ } ++#endif /* CONFIG_FILS */ ++ ++ reply_res = send_assoc_resp(hapd, sta, mgmt->sa, resp, reassoc, pos, ++ left, rssi); ++ os_free(tmp); ++ ++ /* ++ * Remove the station in case tranmission of a success response fails ++ * (the STA was added associated to the driver) or if the station was ++ * previously added unassociated. ++ */ ++ if (sta && ((reply_res != WLAN_STATUS_SUCCESS && ++ resp == WLAN_STATUS_SUCCESS) || sta->added_unassoc)) { ++ hostapd_drv_sta_remove(hapd, sta->addr); ++ sta->added_unassoc = 0; ++ } + } + + +@@ -2007,11 +4133,12 @@ + /* Stop Accounting and IEEE 802.1X sessions, but leave the STA + * authenticated. */ + accounting_sta_stop(hapd, sta); +- ieee802_1x_free_station(sta); ++ ieee802_1x_free_station(hapd, sta); + if (sta->ipaddr) + hostapd_drv_br_delete_ip_neigh(hapd, 4, (u8 *) &sta->ipaddr); + ap_sta_ip6addr_del(hapd, sta); + hostapd_drv_sta_remove(hapd, sta->addr); ++ sta->added_unassoc = 0; + + if (sta->timeout_next == STA_NULLFUNC || + sta->timeout_next == STA_DISASSOC) { +@@ -2023,6 +4150,17 @@ + + mlme_disassociate_indication( + hapd, sta, le_to_host16(mgmt->u.disassoc.reason_code)); ++ ++ /* DMG/IEEE 802.11ad does not use deauthication. Deallocate sta upon ++ * disassociation. */ ++ if (hapd->iface->current_mode && ++ hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211AD) { ++ sta->flags &= ~WLAN_STA_AUTH; ++ wpa_auth_sm_event(sta->wpa_sm, WPA_DEAUTH); ++ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, ++ HOSTAPD_LEVEL_DEBUG, "deauthenticated"); ++ ap_free_sta(hapd, sta); ++ } + } + + +@@ -2086,28 +4224,6 @@ + + + #ifdef CONFIG_IEEE80211W +- +-static int hostapd_sa_query_action(struct hostapd_data *hapd, +- const struct ieee80211_mgmt *mgmt, +- size_t len) +-{ +- const u8 *end; +- +- end = mgmt->u.action.u.sa_query_resp.trans_id + +- WLAN_SA_QUERY_TR_ID_LEN; +- if (((u8 *) mgmt) + len < end) { +- wpa_printf(MSG_DEBUG, "IEEE 802.11: Too short SA Query Action " +- "frame (len=%lu)", (unsigned long) len); +- return 0; +- } +- +- ieee802_11_sa_query_action(hapd, mgmt->sa, +- mgmt->u.action.u.sa_query_resp.action, +- mgmt->u.action.u.sa_query_resp.trans_id); +- return 1; +-} +- +- + static int robust_action_frame(u8 category) + { + return category != WLAN_ACTION_PUBLIC && +@@ -2117,12 +4233,13 @@ + + + static int handle_action(struct hostapd_data *hapd, +- const struct ieee80211_mgmt *mgmt, size_t len) ++ const struct ieee80211_mgmt *mgmt, size_t len, ++ unsigned int freq) + { + struct sta_info *sta; +- sta = ap_get_sta(hapd, mgmt->sa); ++ u8 *action __maybe_unused; + +- if (len < IEEE80211_HDRLEN + 1) { ++ if (len < IEEE80211_HDRLEN + 2 + 1) { + hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_DEBUG, + "handle_action - too short payload (len=%lu)", +@@ -2130,11 +4247,19 @@ + return 0; + } + ++ action = (u8 *) &mgmt->u.action.u; ++ wpa_printf(MSG_DEBUG, "RX_ACTION category %u action %u sa " MACSTR ++ " da " MACSTR " len %d freq %u", ++ mgmt->u.action.category, *action, ++ MAC2STR(mgmt->sa), MAC2STR(mgmt->da), (int) len, freq); ++ ++ sta = ap_get_sta(hapd, mgmt->sa); ++ + if (mgmt->u.action.category != WLAN_ACTION_PUBLIC && + (sta == NULL || !(sta->flags & WLAN_STA_ASSOC))) { + wpa_printf(MSG_DEBUG, "IEEE 802.11: Ignored Action " + "frame (category=%u) from unassociated STA " MACSTR, +- MAC2STR(mgmt->sa), mgmt->u.action.category); ++ mgmt->u.action.category, MAC2STR(mgmt->sa)); + return 0; + } + +@@ -2171,7 +4296,7 @@ + } + + switch (mgmt->u.action.category) { +-#ifdef CONFIG_IEEE80211R ++#ifdef CONFIG_IEEE80211R_AP + case WLAN_ACTION_FT: + if (!sta || + wpa_ft_action_rx(sta->wpa_sm, (u8 *) &mgmt->u.action, +@@ -2178,19 +4303,20 @@ + len - IEEE80211_HDRLEN)) + break; + return 1; +-#endif /* CONFIG_IEEE80211R */ ++#endif /* CONFIG_IEEE80211R_AP */ + case WLAN_ACTION_WMM: + hostapd_wmm_action(hapd, mgmt, len); + return 1; + #ifdef CONFIG_IEEE80211W + case WLAN_ACTION_SA_QUERY: +- return hostapd_sa_query_action(hapd, mgmt, len); ++ ieee802_11_sa_query_action(hapd, mgmt, len); ++ return 1; + #endif /* CONFIG_IEEE80211W */ +-#ifdef CONFIG_WNM ++#ifdef CONFIG_WNM_AP + case WLAN_ACTION_WNM: + ieee802_11_rx_wnm_action_ap(hapd, mgmt, len); + return 1; +-#endif /* CONFIG_WNM */ ++#endif /* CONFIG_WNM_AP */ + #ifdef CONFIG_FST + case WLAN_ACTION_FST: + if (hapd->iface->fst) +@@ -2206,12 +4332,41 @@ + if (len >= IEEE80211_HDRLEN + 2 && + mgmt->u.action.u.public_action.action == + WLAN_PA_20_40_BSS_COEX) { +- wpa_printf(MSG_DEBUG, +- "HT20/40 coex mgmt frame received from STA " +- MACSTR, MAC2STR(mgmt->sa)); + hostapd_2040_coex_action(hapd, mgmt, len); ++ return 1; + } + #endif /* CONFIG_IEEE80211N */ ++#ifdef CONFIG_DPP ++ if (len >= IEEE80211_HDRLEN + 6 && ++ mgmt->u.action.u.vs_public_action.action == ++ WLAN_PA_VENDOR_SPECIFIC && ++ WPA_GET_BE24(mgmt->u.action.u.vs_public_action.oui) == ++ OUI_WFA && ++ mgmt->u.action.u.vs_public_action.variable[0] == ++ DPP_OUI_TYPE) { ++ const u8 *pos, *end; ++ ++ pos = mgmt->u.action.u.vs_public_action.oui; ++ end = ((const u8 *) mgmt) + len; ++ hostapd_dpp_rx_action(hapd, mgmt->sa, pos, end - pos, ++ freq); ++ return 1; ++ } ++ if (len >= IEEE80211_HDRLEN + 2 && ++ (mgmt->u.action.u.public_action.action == ++ WLAN_PA_GAS_INITIAL_RESP || ++ mgmt->u.action.u.public_action.action == ++ WLAN_PA_GAS_COMEBACK_RESP)) { ++ const u8 *pos, *end; ++ ++ pos = &mgmt->u.action.u.public_action.action; ++ end = ((const u8 *) mgmt) + len; ++ gas_query_ap_rx(hapd->gas, mgmt->sa, ++ mgmt->u.action.category, ++ pos, end - pos, hapd->iface->freq); ++ return 1; ++ } ++#endif /* CONFIG_DPP */ + if (hapd->public_action_cb) { + hapd->public_action_cb(hapd->public_action_cb_ctx, + (u8 *) mgmt, len, +@@ -2233,6 +4388,9 @@ + return 1; + } + break; ++ case WLAN_ACTION_RADIO_MEASUREMENT: ++ hostapd_handle_radio_measurement(hapd, (const u8 *) mgmt, len); ++ return 1; + } + + hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211, +@@ -2240,8 +4398,9 @@ + "handle_action - unknown action category %d or invalid " + "frame", + mgmt->u.action.category); +- if (!(mgmt->da[0] & 0x01) && !(mgmt->u.action.category & 0x80) && +- !(mgmt->sa[0] & 0x01)) { ++ if (!is_multicast_ether_addr(mgmt->da) && ++ !(mgmt->u.action.category & 0x80) && ++ !is_multicast_ether_addr(mgmt->sa)) { + struct ieee80211_mgmt *resp; + + /* +@@ -2251,10 +4410,9 @@ + */ + wpa_printf(MSG_DEBUG, "IEEE 802.11: Return unknown Action " + "frame back to sender"); +- resp = os_malloc(len); ++ resp = os_memdup(mgmt, len); + if (resp == NULL) + return 0; +- os_memcpy(resp, mgmt, len); + os_memcpy(resp->da, resp->sa, ETH_ALEN); + os_memcpy(resp->sa, hapd->own_addr, ETH_ALEN); + os_memcpy(resp->bssid, hapd->own_addr, ETH_ALEN); +@@ -2288,13 +4446,19 @@ + struct hostapd_frame_info *fi) + { + struct ieee80211_mgmt *mgmt; +- int broadcast; + u16 fc, stype; + int ret = 0; ++ unsigned int freq; ++ int ssi_signal = fi ? fi->ssi_signal : 0; + + if (len < 24) + return 0; + ++ if (fi && fi->freq) ++ freq = fi->freq; ++ else ++ freq = hapd->iface->freq; ++ + mgmt = (struct ieee80211_mgmt *) buf; + fc = le_to_host16(mgmt->frame_control); + stype = WLAN_FC_GET_STYPE(fc); +@@ -2304,11 +4468,7 @@ + return 1; + } + +- broadcast = mgmt->bssid[0] == 0xff && mgmt->bssid[1] == 0xff && +- mgmt->bssid[2] == 0xff && mgmt->bssid[3] == 0xff && +- mgmt->bssid[4] == 0xff && mgmt->bssid[5] == 0xff; +- +- if (!broadcast && ++ if (!is_broadcast_ether_addr(mgmt->bssid) && + #ifdef CONFIG_P2P + /* Invitation responses can be sent with the peer MAC as BSSID */ + !((hapd->conf->p2p & P2P_GROUP_OWNER) && +@@ -2325,11 +4485,13 @@ + + + if (stype == WLAN_FC_STYPE_PROBE_REQ) { +- handle_probe_req(hapd, mgmt, len, fi->ssi_signal); ++ handle_probe_req(hapd, mgmt, len, ssi_signal); + return 1; + } + +- if (os_memcmp(mgmt->da, hapd->own_addr, ETH_ALEN) != 0) { ++ if ((!is_broadcast_ether_addr(mgmt->da) || ++ stype != WLAN_FC_STYPE_ACTION) && ++ os_memcmp(mgmt->da, hapd->own_addr, ETH_ALEN) != 0) { + hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_DEBUG, + "MGMT: DA=" MACSTR " not our address", +@@ -2338,22 +4500,22 @@ + } + + if (hapd->iconf->track_sta_max_num) +- sta_track_add(hapd->iface, mgmt->sa); ++ sta_track_add(hapd->iface, mgmt->sa, ssi_signal); + + switch (stype) { + case WLAN_FC_STYPE_AUTH: + wpa_printf(MSG_DEBUG, "mgmt::auth"); +- handle_auth(hapd, mgmt, len); ++ handle_auth(hapd, mgmt, len, ssi_signal, 0); + ret = 1; + break; + case WLAN_FC_STYPE_ASSOC_REQ: + wpa_printf(MSG_DEBUG, "mgmt::assoc_req"); +- handle_assoc(hapd, mgmt, len, 0); ++ handle_assoc(hapd, mgmt, len, 0, ssi_signal); + ret = 1; + break; + case WLAN_FC_STYPE_REASSOC_REQ: + wpa_printf(MSG_DEBUG, "mgmt::reassoc_req"); +- handle_assoc(hapd, mgmt, len, 1); ++ handle_assoc(hapd, mgmt, len, 1, ssi_signal); + ret = 1; + break; + case WLAN_FC_STYPE_DISASSOC: +@@ -2368,7 +4530,7 @@ + break; + case WLAN_FC_STYPE_ACTION: + wpa_printf(MSG_DEBUG, "mgmt::action"); +- ret = handle_action(hapd, mgmt, len); ++ ret = handle_action(hapd, mgmt, len, freq); + break; + default: + hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211, +@@ -2388,30 +4550,31 @@ + u16 auth_alg, auth_transaction, status_code; + struct sta_info *sta; + ++ sta = ap_get_sta(hapd, mgmt->da); ++ if (!sta) { ++ wpa_printf(MSG_DEBUG, "handle_auth_cb: STA " MACSTR ++ " not found", ++ MAC2STR(mgmt->da)); ++ return; ++ } ++ ++ auth_alg = le_to_host16(mgmt->u.auth.auth_alg); ++ auth_transaction = le_to_host16(mgmt->u.auth.auth_transaction); ++ status_code = le_to_host16(mgmt->u.auth.status_code); ++ + if (!ok) { + hostapd_logger(hapd, mgmt->da, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_NOTICE, + "did not acknowledge authentication response"); +- return; ++ goto fail; + } + + if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.auth)) { + wpa_printf(MSG_INFO, "handle_auth_cb - too short payload (len=%lu)", + (unsigned long) len); +- return; ++ goto fail; + } + +- auth_alg = le_to_host16(mgmt->u.auth.auth_alg); +- auth_transaction = le_to_host16(mgmt->u.auth.auth_transaction); +- status_code = le_to_host16(mgmt->u.auth.status_code); +- +- sta = ap_get_sta(hapd, mgmt->da); +- if (!sta) { +- wpa_printf(MSG_INFO, "handle_auth_cb: STA " MACSTR " not found", +- MAC2STR(mgmt->da)); +- return; +- } +- + if (status_code == WLAN_STATUS_SUCCESS && + ((auth_alg == WLAN_AUTH_OPEN && auth_transaction == 2) || + (auth_alg == WLAN_AUTH_SHARED_KEY && auth_transaction == 4))) { +@@ -2418,7 +4581,16 @@ + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_INFO, "authenticated"); + sta->flags |= WLAN_STA_AUTH; ++ if (sta->added_unassoc) ++ hostapd_set_sta_flags(hapd, sta); ++ return; + } ++ ++fail: ++ if (status_code != WLAN_STATUS_SUCCESS && sta->added_unassoc) { ++ hostapd_drv_sta_remove(hapd, sta->addr); ++ sta->added_unassoc = 0; ++ } + } + + +@@ -2453,16 +4625,7 @@ + u16 status; + struct sta_info *sta; + int new_assoc = 1; +- struct ieee80211_ht_capabilities ht_cap; +- struct ieee80211_vht_capabilities vht_cap; + +- if (len < IEEE80211_HDRLEN + (reassoc ? sizeof(mgmt->u.reassoc_resp) : +- sizeof(mgmt->u.assoc_resp))) { +- wpa_printf(MSG_INFO, "handle_assoc_cb(reassoc=%d) - too short payload (len=%lu)", +- reassoc, (unsigned long) len); +- return; +- } +- + sta = ap_get_sta(hapd, mgmt->da); + if (!sta) { + wpa_printf(MSG_INFO, "handle_assoc_cb: STA " MACSTR " not found", +@@ -2470,19 +4633,32 @@ + return; + } + ++ if (len < IEEE80211_HDRLEN + (reassoc ? sizeof(mgmt->u.reassoc_resp) : ++ sizeof(mgmt->u.assoc_resp))) { ++ wpa_printf(MSG_INFO, ++ "handle_assoc_cb(reassoc=%d) - too short payload (len=%lu)", ++ reassoc, (unsigned long) len); ++ hostapd_drv_sta_remove(hapd, sta->addr); ++ return; ++ } ++ ++ if (reassoc) ++ status = le_to_host16(mgmt->u.reassoc_resp.status_code); ++ else ++ status = le_to_host16(mgmt->u.assoc_resp.status_code); ++ + if (!ok) { + hostapd_logger(hapd, mgmt->da, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_DEBUG, + "did not acknowledge association response"); + sta->flags &= ~WLAN_STA_ASSOC_REQ_OK; ++ /* The STA is added only in case of SUCCESS */ ++ if (status == WLAN_STATUS_SUCCESS) ++ hostapd_drv_sta_remove(hapd, sta->addr); ++ + return; + } + +- if (reassoc) +- status = le_to_host16(mgmt->u.reassoc_resp.status_code); +- else +- status = le_to_host16(mgmt->u.assoc_resp.status_code); +- + if (status != WLAN_STATUS_SUCCESS) + return; + +@@ -2499,11 +4675,15 @@ + new_assoc = 0; + sta->flags |= WLAN_STA_ASSOC; + sta->flags &= ~WLAN_STA_WNM_SLEEP_MODE; +- if ((!hapd->conf->ieee802_1x && !hapd->conf->wpa && !hapd->conf->osen) || ++ if ((!hapd->conf->ieee802_1x && !hapd->conf->wpa && ++ !hapd->conf->osen) || ++ sta->auth_alg == WLAN_AUTH_FILS_SK || ++ sta->auth_alg == WLAN_AUTH_FILS_SK_PFS || ++ sta->auth_alg == WLAN_AUTH_FILS_PK || + sta->auth_alg == WLAN_AUTH_FT) { + /* +- * Open, static WEP, or FT protocol; no separate authorization +- * step. ++ * Open, static WEP, FT protocol, or FILS; no separate ++ * authorization step. + */ + ap_sta_set_authorized(hapd, sta, 1); + } +@@ -2517,48 +4697,6 @@ + sta->sa_query_timed_out = 0; + #endif /* CONFIG_IEEE80211W */ + +- /* +- * Remove the STA entry in order to make sure the STA PS state gets +- * cleared and configuration gets updated in case of reassociation back +- * to the same AP. +- */ +- hostapd_drv_sta_remove(hapd, sta->addr); +- +-#ifdef CONFIG_IEEE80211N +- if (sta->flags & WLAN_STA_HT) +- hostapd_get_ht_capab(hapd, sta->ht_capabilities, &ht_cap); +-#endif /* CONFIG_IEEE80211N */ +-#ifdef CONFIG_IEEE80211AC +- if (sta->flags & WLAN_STA_VHT) +- hostapd_get_vht_capab(hapd, sta->vht_capabilities, &vht_cap); +-#endif /* CONFIG_IEEE80211AC */ +- +- if (hostapd_sta_add(hapd, sta->addr, sta->aid, sta->capability, +- sta->supported_rates, sta->supported_rates_len, +- sta->listen_interval, +- sta->flags & WLAN_STA_HT ? &ht_cap : NULL, +- sta->flags & WLAN_STA_VHT ? &vht_cap : NULL, +- sta->flags, sta->qosinfo, sta->vht_opmode)) { +- hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, +- HOSTAPD_LEVEL_NOTICE, +- "Could not add STA to kernel driver"); +- +- ap_sta_disconnect(hapd, sta, sta->addr, +- WLAN_REASON_DISASSOC_AP_BUSY); +- +- return; +- } +- +- if (sta->flags & WLAN_STA_WDS) { +- int ret; +- char ifname_wds[IFNAMSIZ + 1]; +- +- ret = hostapd_set_wds_sta(hapd, ifname_wds, sta->addr, +- sta->aid, 1); +- if (!ret) +- hostapd_set_wds_encryption(hapd, sta, ifname_wds); +- } +- + if (sta->eapol_sm == NULL) { + /* + * This STA does not use RADIUS server for EAP authentication, +@@ -2575,13 +4713,64 @@ + + hostapd_set_sta_flags(hapd, sta); + ++ if (!(sta->flags & WLAN_STA_WDS) && sta->pending_wds_enable) { ++ wpa_printf(MSG_DEBUG, "Enable 4-address WDS mode for STA " ++ MACSTR " based on pending request", ++ MAC2STR(sta->addr)); ++ sta->pending_wds_enable = 0; ++ sta->flags |= WLAN_STA_WDS; ++ } ++ ++ if (sta->flags & (WLAN_STA_WDS | WLAN_STA_MULTI_AP)) { ++ int ret; ++ char ifname_wds[IFNAMSIZ + 1]; ++ ++ wpa_printf(MSG_DEBUG, "Reenable 4-address WDS mode for STA " ++ MACSTR " (aid %u)", ++ MAC2STR(sta->addr), sta->aid); ++ ret = hostapd_set_wds_sta(hapd, ifname_wds, sta->addr, ++ sta->aid, 1); ++ if (!ret) ++ hostapd_set_wds_encryption(hapd, sta, ifname_wds); ++ } ++ + if (sta->auth_alg == WLAN_AUTH_FT) + wpa_auth_sm_event(sta->wpa_sm, WPA_ASSOC_FT); + else + wpa_auth_sm_event(sta->wpa_sm, WPA_ASSOC); + hapd->new_assoc_sta_cb(hapd, sta, !new_assoc); ++ ieee802_1x_notify_port_enabled(sta->eapol_sm, 1); + +- ieee802_1x_notify_port_enabled(sta->eapol_sm, 1); ++#ifdef CONFIG_FILS ++ if ((sta->auth_alg == WLAN_AUTH_FILS_SK || ++ sta->auth_alg == WLAN_AUTH_FILS_SK_PFS || ++ sta->auth_alg == WLAN_AUTH_FILS_PK) && ++ fils_set_tk(sta->wpa_sm) < 0) { ++ wpa_printf(MSG_DEBUG, "FILS: TK configuration failed"); ++ ap_sta_disconnect(hapd, sta, sta->addr, ++ WLAN_REASON_UNSPECIFIED); ++ return; ++ } ++#endif /* CONFIG_FILS */ ++ ++ if (sta->pending_eapol_rx) { ++ struct os_reltime now, age; ++ ++ os_get_reltime(&now); ++ os_reltime_sub(&now, &sta->pending_eapol_rx->rx_time, &age); ++ if (age.sec == 0 && age.usec < 200000) { ++ wpa_printf(MSG_DEBUG, ++ "Process pending EAPOL frame that was received from " MACSTR " just before association notification", ++ MAC2STR(sta->addr)); ++ ieee802_1x_receive( ++ hapd, mgmt->da, ++ wpabuf_head(sta->pending_eapol_rx->buf), ++ wpabuf_len(sta->pending_eapol_rx->buf)); ++ } ++ wpabuf_free(sta->pending_eapol_rx->buf); ++ os_free(sta->pending_eapol_rx); ++ sta->pending_eapol_rx = NULL; ++ } + } + + +@@ -2590,7 +4779,7 @@ + size_t len, int ok) + { + struct sta_info *sta; +- if (mgmt->da[0] & 0x01) ++ if (is_multicast_ether_addr(mgmt->da)) + return; + sta = ap_get_sta(hapd, mgmt->da); + if (!sta) { +@@ -2614,7 +4803,7 @@ + size_t len, int ok) + { + struct sta_info *sta; +- if (mgmt->da[0] & 0x01) ++ if (is_multicast_ether_addr(mgmt->da)) + return; + sta = ap_get_sta(hapd, mgmt->da); + if (!sta) { +@@ -2633,6 +4822,65 @@ + } + + ++static void handle_action_cb(struct hostapd_data *hapd, ++ const struct ieee80211_mgmt *mgmt, ++ size_t len, int ok) ++{ ++ struct sta_info *sta; ++ const struct rrm_measurement_report_element *report; ++ ++ if (is_multicast_ether_addr(mgmt->da)) ++ return; ++#ifdef CONFIG_DPP ++ if (len >= IEEE80211_HDRLEN + 6 && ++ mgmt->u.action.category == WLAN_ACTION_PUBLIC && ++ mgmt->u.action.u.vs_public_action.action == ++ WLAN_PA_VENDOR_SPECIFIC && ++ WPA_GET_BE24(mgmt->u.action.u.vs_public_action.oui) == ++ OUI_WFA && ++ mgmt->u.action.u.vs_public_action.variable[0] == ++ DPP_OUI_TYPE) { ++ const u8 *pos, *end; ++ ++ pos = &mgmt->u.action.u.vs_public_action.variable[1]; ++ end = ((const u8 *) mgmt) + len; ++ hostapd_dpp_tx_status(hapd, mgmt->da, pos, end - pos, ok); ++ return; ++ } ++ if (len >= IEEE80211_HDRLEN + 2 && ++ mgmt->u.action.category == WLAN_ACTION_PUBLIC && ++ (mgmt->u.action.u.public_action.action == ++ WLAN_PA_GAS_INITIAL_REQ || ++ mgmt->u.action.u.public_action.action == ++ WLAN_PA_GAS_COMEBACK_REQ)) { ++ const u8 *pos, *end; ++ ++ pos = mgmt->u.action.u.public_action.variable; ++ end = ((const u8 *) mgmt) + len; ++ gas_query_ap_tx_status(hapd->gas, mgmt->da, pos, end - pos, ok); ++ return; ++ } ++#endif /* CONFIG_DPP */ ++ sta = ap_get_sta(hapd, mgmt->da); ++ if (!sta) { ++ wpa_printf(MSG_DEBUG, "handle_action_cb: STA " MACSTR ++ " not found", MAC2STR(mgmt->da)); ++ return; ++ } ++ ++ if (len < 24 + 5 + sizeof(*report)) ++ return; ++ report = (const struct rrm_measurement_report_element *) ++ &mgmt->u.action.u.rrm.variable[2]; ++ if (mgmt->u.action.category == WLAN_ACTION_RADIO_MEASUREMENT && ++ mgmt->u.action.u.rrm.action == WLAN_RRM_RADIO_MEASUREMENT_REQUEST && ++ report->eid == WLAN_EID_MEASURE_REQUEST && ++ report->len >= 3 && ++ report->type == MEASURE_TYPE_BEACON) ++ hostapd_rrm_beacon_req_tx_status(hapd, mgmt, len, ok); ++} ++ ++ + /** + * ieee802_11_mgmt_cb - Process management frame TX status callback + * @hapd: hostapd BSS data structure (the BSS from which the management frame +@@ -2650,8 +4898,16 @@ + + #ifdef CONFIG_TESTING_OPTIONS + if (hapd->ext_mgmt_frame_handling) { +- wpa_msg(hapd->msg_ctx, MSG_INFO, "MGMT-TX-STATUS stype=%u ok=%d", +- stype, ok); ++ size_t hex_len = 2 * len + 1; ++ char *hex = os_malloc(hex_len); ++ ++ if (hex) { ++ wpa_snprintf_hex(hex, hex_len, buf, len); ++ wpa_msg(hapd->msg_ctx, MSG_INFO, ++ "MGMT-TX-STATUS stype=%u ok=%d buf=%s", ++ stype, ok, hex); ++ os_free(hex); ++ } + return; + } + #endif /* CONFIG_TESTING_OPTIONS */ +@@ -2670,7 +4926,7 @@ + handle_assoc_cb(hapd, mgmt, len, 1, ok); + break; + case WLAN_FC_STYPE_PROBE_RESP: +- wpa_printf(MSG_EXCESSIVE, "mgmt::proberesp cb"); ++ wpa_printf(MSG_EXCESSIVE, "mgmt::proberesp cb ok=%d", ok); + break; + case WLAN_FC_STYPE_DEAUTH: + wpa_printf(MSG_DEBUG, "mgmt::deauth cb"); +@@ -2681,7 +4937,8 @@ + handle_disassoc_cb(hapd, mgmt, len, ok); + break; + case WLAN_FC_STYPE_ACTION: +- wpa_printf(MSG_DEBUG, "mgmt::action cb"); ++ wpa_printf(MSG_DEBUG, "mgmt::action cb ok=%d", ok); ++ handle_action_cb(hapd, mgmt, len, ok); + break; + default: + wpa_printf(MSG_INFO, "unknown mgmt cb frame subtype %d", stype); +@@ -2779,6 +5036,8 @@ + } + if (sta == NULL) + return; ++ wpa_msg(hapd->msg_ctx, MSG_INFO, AP_STA_POLL_OK MACSTR, ++ MAC2STR(sta->addr)); + if (!(sta->flags & WLAN_STA_PENDING_POLL)) + return; + +@@ -2794,10 +5053,22 @@ + struct sta_info *sta; + + sta = ap_get_sta(hapd, src); +- if (sta && (sta->flags & WLAN_STA_ASSOC)) { ++ if (sta && ++ ((sta->flags & WLAN_STA_ASSOC) || ++ ((sta->flags & WLAN_STA_ASSOC_REQ_OK) && wds))) { + if (!hapd->conf->wds_sta) + return; + ++ if ((sta->flags & (WLAN_STA_ASSOC | WLAN_STA_ASSOC_REQ_OK)) == ++ WLAN_STA_ASSOC_REQ_OK) { ++ wpa_printf(MSG_DEBUG, ++ "Postpone 4-address WDS mode enabling for STA " ++ MACSTR " since TX status for AssocResp is not yet known", ++ MAC2STR(sta->addr)); ++ sta->pending_wds_enable = 1; ++ return; ++ } ++ + if (wds && !(sta->flags & WLAN_STA_WDS)) { + int ret; + char ifname_wds[IFNAMSIZ + 1]; +@@ -2817,7 +5088,7 @@ + + wpa_printf(MSG_DEBUG, "Data/PS-poll frame from not associated STA " + MACSTR, MAC2STR(src)); +- if (src[0] & 0x01) { ++ if (is_multicast_ether_addr(src)) { + /* Broadcast bit set in SA?! Ignore the frame silently. */ + return; + } +--- contrib/wpa/src/ap/ieee802_11.h.orig ++++ contrib/wpa/src/ap/ieee802_11.h +@@ -16,6 +16,8 @@ + struct ieee80211_ht_capabilities; + struct ieee80211_vht_capabilities; + struct ieee80211_mgmt; ++struct vlan_description; ++struct hostapd_sta_wpa_psk_short; + + int ieee802_11_mgmt(struct hostapd_data *hapd, const u8 *buf, size_t len, + struct hostapd_frame_info *fi); +@@ -49,9 +51,16 @@ + u8 * hostapd_eid_ext_supp_rates(struct hostapd_data *hapd, u8 *eid); + u8 * hostapd_eid_ht_capabilities(struct hostapd_data *hapd, u8 *eid); + u8 * hostapd_eid_ht_operation(struct hostapd_data *hapd, u8 *eid); +-u8 * hostapd_eid_vht_capabilities(struct hostapd_data *hapd, u8 *eid); ++u8 * hostapd_eid_secondary_channel(struct hostapd_data *hapd, u8 *eid); ++u8 * hostapd_eid_vht_capabilities(struct hostapd_data *hapd, u8 *eid, u32 nsts); + u8 * hostapd_eid_vht_operation(struct hostapd_data *hapd, u8 *eid); + u8 * hostapd_eid_vendor_vht(struct hostapd_data *hapd, u8 *eid); ++u8 * hostapd_eid_wb_chsw_wrapper(struct hostapd_data *hapd, u8 *eid); ++u8 * hostapd_eid_txpower_envelope(struct hostapd_data *hapd, u8 *eid); ++u8 * hostapd_eid_he_capab(struct hostapd_data *hapd, u8 *eid); ++u8 * hostapd_eid_he_operation(struct hostapd_data *hapd, u8 *eid); ++u8 * hostapd_eid_he_mu_edca_parameter_set(struct hostapd_data *hapd, u8 *eid); ++ + int hostapd_ht_operation_update(struct hostapd_iface *iface); + void ieee802_11_send_sa_query_req(struct hostapd_data *hapd, + const u8 *addr, const u8 *trans_id); +@@ -61,6 +70,7 @@ + void hostapd_get_vht_capab(struct hostapd_data *hapd, + struct ieee80211_vht_capabilities *vht_cap, + struct ieee80211_vht_capabilities *neg_vht_cap); ++int hostapd_get_aid(struct hostapd_data *hapd, struct sta_info *sta); + u16 copy_sta_ht_capab(struct hostapd_data *hapd, struct sta_info *sta, + const u8 *ht_capab); + u16 copy_sta_vendor_vht(struct hostapd_data *hapd, struct sta_info *sta, +@@ -71,6 +81,8 @@ + void ht40_intolerant_remove(struct hostapd_iface *iface, struct sta_info *sta); + u16 copy_sta_vht_capab(struct hostapd_data *hapd, struct sta_info *sta, + const u8 *vht_capab); ++u16 copy_sta_vht_oper(struct hostapd_data *hapd, struct sta_info *sta, ++ const u8 *vht_oper); + u16 set_sta_vht_opmode(struct hostapd_data *hapd, struct sta_info *sta, + const u8 *vht_opmode); + void hostapd_tx_status(struct hostapd_data *hapd, const u8 *addr, +@@ -82,8 +94,8 @@ + u8 * hostapd_eid_assoc_comeback_time(struct hostapd_data *hapd, + struct sta_info *sta, u8 *eid); + void ieee802_11_sa_query_action(struct hostapd_data *hapd, +- const u8 *sa, const u8 action_type, +- const u8 *trans_id); ++ const struct ieee80211_mgmt *mgmt, ++ size_t len); + u8 * hostapd_eid_interworking(struct hostapd_data *hapd, u8 *eid); + u8 * hostapd_eid_adv_proto(struct hostapd_data *hapd, u8 *eid); + u8 * hostapd_eid_roaming_consortium(struct hostapd_data *hapd, u8 *eid); +@@ -97,6 +109,7 @@ + #ifdef CONFIG_SAE + void sae_clear_retransmit_timer(struct hostapd_data *hapd, + struct sta_info *sta); ++void sae_accept_sta(struct hostapd_data *hapd, struct sta_info *sta); + #else /* CONFIG_SAE */ + static inline void sae_clear_retransmit_timer(struct hostapd_data *hapd, + struct sta_info *sta) +@@ -104,4 +117,64 @@ + } + #endif /* CONFIG_SAE */ + ++#ifdef CONFIG_MBO ++ ++u8 * hostapd_eid_mbo(struct hostapd_data *hapd, u8 *eid, size_t len); ++ ++u8 hostapd_mbo_ie_len(struct hostapd_data *hapd); ++ ++u8 * hostapd_eid_mbo_rssi_assoc_rej(struct hostapd_data *hapd, u8 *eid, ++ size_t len, int delta); ++ ++#else /* CONFIG_MBO */ ++ ++static inline u8 * hostapd_eid_mbo(struct hostapd_data *hapd, u8 *eid, ++ size_t len) ++{ ++ return eid; ++} ++ ++static inline u8 hostapd_mbo_ie_len(struct hostapd_data *hapd) ++{ ++ return 0; ++} ++ ++#endif /* CONFIG_MBO */ ++ ++void ap_copy_sta_supp_op_classes(struct sta_info *sta, ++ const u8 *supp_op_classes, ++ size_t supp_op_classes_len); ++ ++u8 * hostapd_eid_fils_indic(struct hostapd_data *hapd, u8 *eid, int hessid); ++void ieee802_11_finish_fils_auth(struct hostapd_data *hapd, ++ struct sta_info *sta, int success, ++ struct wpabuf *erp_resp, ++ const u8 *msk, size_t msk_len); ++u8 * owe_assoc_req_process(struct hostapd_data *hapd, struct sta_info *sta, ++ const u8 *owe_dh, u8 owe_dh_len, ++ u8 *owe_buf, size_t owe_buf_len, u16 *reason); ++void fils_hlp_timeout(void *eloop_ctx, void *eloop_data); ++void fils_hlp_finish_assoc(struct hostapd_data *hapd, struct sta_info *sta); ++void handle_auth_fils(struct hostapd_data *hapd, struct sta_info *sta, ++ const u8 *pos, size_t len, u16 auth_alg, ++ u16 auth_transaction, u16 status_code, ++ void (*cb)(struct hostapd_data *hapd, ++ struct sta_info *sta, ++ u16 resp, struct wpabuf *data, int pub)); ++ ++size_t hostapd_eid_owe_trans_len(struct hostapd_data *hapd); ++u8 * hostapd_eid_owe_trans(struct hostapd_data *hapd, u8 *eid, size_t len); ++int ieee802_11_allowed_address(struct hostapd_data *hapd, const u8 *addr, ++ const u8 *msg, size_t len, u32 *session_timeout, ++ u32 *acct_interim_interval, ++ struct vlan_description *vlan_id, ++ struct hostapd_sta_wpa_psk_short **psk, ++ char **identity, char **radius_cui, ++ int is_probe_req); ++ ++int get_tx_parameters(struct sta_info *sta, int ap_max_chanwidth, ++ int ap_seg1_idx, int *bandwidth, int *seg1_idx); ++ ++void auth_sae_process_commit(void *eloop_ctx, void *user_ctx); ++ + #endif /* IEEE802_11_H */ +--- contrib/wpa/src/ap/ieee802_11_auth.c.orig ++++ contrib/wpa/src/ap/ieee802_11_auth.c +@@ -15,7 +15,6 @@ + + #include "utils/common.h" + #include "utils/eloop.h" +-#include "crypto/sha1.h" + #include "radius/radius.h" + #include "radius/radius_client.h" + #include "hostapd.h" +@@ -35,7 +34,7 @@ + struct hostapd_cached_radius_acl *next; + u32 session_timeout; + u32 acct_interim_interval; +- int vlan_id; ++ struct vlan_description vlan_id; + struct hostapd_sta_wpa_psk_short *psk; + char *identity; + char *radius_cui; +@@ -77,29 +76,20 @@ + static void copy_psk_list(struct hostapd_sta_wpa_psk_short **psk, + struct hostapd_sta_wpa_psk_short *src) + { +- struct hostapd_sta_wpa_psk_short **copy_to; +- struct hostapd_sta_wpa_psk_short *copy_from; ++ if (!psk) ++ return; + +- /* Copy PSK linked list */ +- copy_to = psk; +- copy_from = src; +- while (copy_from && copy_to) { +- *copy_to = os_zalloc(sizeof(struct hostapd_sta_wpa_psk_short)); +- if (*copy_to == NULL) +- break; +- os_memcpy(*copy_to, copy_from, +- sizeof(struct hostapd_sta_wpa_psk_short)); +- copy_from = copy_from->next; +- copy_to = &((*copy_to)->next); +- } +- if (copy_to) +- *copy_to = NULL; ++ if (src) ++ src->ref++; ++ ++ *psk = src; + } + + + static int hostapd_acl_cache_get(struct hostapd_data *hapd, const u8 *addr, + u32 *session_timeout, +- u32 *acct_interim_interval, int *vlan_id, ++ u32 *acct_interim_interval, ++ struct vlan_description *vlan_id, + struct hostapd_sta_wpa_psk_short **psk, + char **identity, char **radius_cui) + { +@@ -165,7 +155,10 @@ + if (msg == NULL) + return -1; + +- radius_msg_make_authenticator(msg, addr, ETH_ALEN); ++ if (radius_msg_make_authenticator(msg) < 0) { ++ wpa_printf(MSG_INFO, "Could not make Request Authenticator"); ++ goto fail; ++ } + + os_snprintf(buf, sizeof(buf), RADIUS_ADDR_FORMAT, MAC2STR(addr)); + if (!radius_msg_add_attr(msg, RADIUS_ATTR_USER_NAME, (u8 *) buf, +@@ -213,6 +206,33 @@ + + + /** ++ * hostapd_check_acl - Check a specified STA against accept/deny ACLs ++ * @hapd: hostapd BSS data ++ * @addr: MAC address of the STA ++ * @vlan_id: Buffer for returning VLAN ID ++ * Returns: HOSTAPD_ACL_ACCEPT, HOSTAPD_ACL_REJECT, or HOSTAPD_ACL_PENDING ++ */ ++int hostapd_check_acl(struct hostapd_data *hapd, const u8 *addr, ++ struct vlan_description *vlan_id) ++{ ++ if (hostapd_maclist_found(hapd->conf->accept_mac, ++ hapd->conf->num_accept_mac, addr, vlan_id)) ++ return HOSTAPD_ACL_ACCEPT; ++ ++ if (hostapd_maclist_found(hapd->conf->deny_mac, ++ hapd->conf->num_deny_mac, addr, vlan_id)) ++ return HOSTAPD_ACL_REJECT; ++ ++ if (hapd->conf->macaddr_acl == ACCEPT_UNLESS_DENIED) ++ return HOSTAPD_ACL_ACCEPT; ++ if (hapd->conf->macaddr_acl == DENY_UNLESS_ACCEPTED) ++ return HOSTAPD_ACL_REJECT; ++ ++ return HOSTAPD_ACL_PENDING; ++} ++ ++ ++/** + * hostapd_allowed_address - Check whether a specified STA can be authenticated + * @hapd: hostapd BSS data + * @addr: MAC address of the STA +@@ -224,6 +244,7 @@ + * @psk: Linked list buffer for returning WPA PSK + * @identity: Buffer for returning identity (from RADIUS) + * @radius_cui: Buffer for returning CUI (from RADIUS) ++ * @is_probe_req: Whether this query for a Probe Request frame + * Returns: HOSTAPD_ACL_ACCEPT, HOSTAPD_ACL_REJECT, or HOSTAPD_ACL_PENDING + * + * The caller is responsible for freeing the returned *identity and *radius_cui +@@ -231,16 +252,20 @@ + */ + int hostapd_allowed_address(struct hostapd_data *hapd, const u8 *addr, + const u8 *msg, size_t len, u32 *session_timeout, +- u32 *acct_interim_interval, int *vlan_id, ++ u32 *acct_interim_interval, ++ struct vlan_description *vlan_id, + struct hostapd_sta_wpa_psk_short **psk, +- char **identity, char **radius_cui) ++ char **identity, char **radius_cui, ++ int is_probe_req) + { ++ int res; ++ + if (session_timeout) + *session_timeout = 0; + if (acct_interim_interval) + *acct_interim_interval = 0; + if (vlan_id) +- *vlan_id = 0; ++ os_memset(vlan_id, 0, sizeof(*vlan_id)); + if (psk) + *psk = NULL; + if (identity) +@@ -248,19 +273,10 @@ + if (radius_cui) + *radius_cui = NULL; + +- if (hostapd_maclist_found(hapd->conf->accept_mac, +- hapd->conf->num_accept_mac, addr, vlan_id)) +- return HOSTAPD_ACL_ACCEPT; ++ res = hostapd_check_acl(hapd, addr, vlan_id); ++ if (res != HOSTAPD_ACL_PENDING) ++ return res; + +- if (hostapd_maclist_found(hapd->conf->deny_mac, +- hapd->conf->num_deny_mac, addr, vlan_id)) +- return HOSTAPD_ACL_REJECT; +- +- if (hapd->conf->macaddr_acl == ACCEPT_UNLESS_DENIED) +- return HOSTAPD_ACL_ACCEPT; +- if (hapd->conf->macaddr_acl == DENY_UNLESS_ACCEPTED) +- return HOSTAPD_ACL_REJECT; +- + if (hapd->conf->macaddr_acl == USE_EXTERNAL_RADIUS_AUTH) { + #ifdef CONFIG_NO_RADIUS + return HOSTAPD_ACL_REJECT; +@@ -267,11 +283,19 @@ + #else /* CONFIG_NO_RADIUS */ + struct hostapd_acl_query_data *query; + ++ if (is_probe_req) { ++ /* Skip RADIUS queries for Probe Request frames to avoid ++ * excessive load on the authentication server. */ ++ return HOSTAPD_ACL_ACCEPT; ++ }; ++ ++ if (hapd->conf->ssid.dynamic_vlan == DYNAMIC_VLAN_DISABLED) ++ vlan_id = NULL; ++ + /* Check whether ACL cache has an entry for this station */ +- int res = hostapd_acl_cache_get(hapd, addr, session_timeout, +- acct_interim_interval, +- vlan_id, psk, +- identity, radius_cui); ++ res = hostapd_acl_cache_get(hapd, addr, session_timeout, ++ acct_interim_interval, vlan_id, psk, ++ identity, radius_cui); + if (res == HOSTAPD_ACL_ACCEPT || + res == HOSTAPD_ACL_ACCEPT_TIMEOUT) + return res; +@@ -314,7 +338,7 @@ + return HOSTAPD_ACL_REJECT; + } + +- query->auth_msg = os_malloc(len); ++ query->auth_msg = os_memdup(msg, len); + if (query->auth_msg == NULL) { + wpa_printf(MSG_ERROR, "Failed to allocate memory for " + "auth frame."); +@@ -321,7 +345,6 @@ + hostapd_acl_query_free(query); + return HOSTAPD_ACL_REJECT; + } +- os_memcpy(query->auth_msg, msg, len); + query->auth_msg_len = len; + query->next = hapd->acl_queries; + hapd->acl_queries = query; +@@ -419,7 +442,7 @@ + struct hostapd_cached_radius_acl *cache) + { + int passphraselen; +- char *passphrase, *strpassphrase; ++ char *passphrase; + size_t i; + struct hostapd_sta_wpa_psk_short *psk; + +@@ -436,24 +459,42 @@ + */ + if (passphrase == NULL) + break; ++ + /* ++ * Passphase should be 8..63 chars (to be hashed with SSID) ++ * or 64 chars hex string (no separate hashing with SSID). ++ */ ++ ++ if (passphraselen < MIN_PASSPHRASE_LEN || ++ passphraselen > MAX_PASSPHRASE_LEN + 1) ++ goto free_pass; ++ ++ /* + * passphrase does not contain the NULL termination. + * Add it here as pbkdf2_sha1() requires it. + */ +- strpassphrase = os_zalloc(passphraselen + 1); + psk = os_zalloc(sizeof(struct hostapd_sta_wpa_psk_short)); +- if (strpassphrase && psk) { +- os_memcpy(strpassphrase, passphrase, passphraselen); +- pbkdf2_sha1(strpassphrase, +- hapd->conf->ssid.ssid, +- hapd->conf->ssid.ssid_len, 4096, +- psk->psk, PMK_LEN); ++ if (psk) { ++ if ((passphraselen == MAX_PASSPHRASE_LEN + 1) && ++ (hexstr2bin(passphrase, psk->psk, PMK_LEN) < 0)) { ++ hostapd_logger(hapd, cache->addr, ++ HOSTAPD_MODULE_RADIUS, ++ HOSTAPD_LEVEL_WARNING, ++ "invalid hex string (%d chars) in Tunnel-Password", ++ passphraselen); ++ goto skip; ++ } else if (passphraselen <= MAX_PASSPHRASE_LEN) { ++ os_memcpy(psk->passphrase, passphrase, ++ passphraselen); ++ psk->is_passphrase = 1; ++ } + psk->next = cache->psk; + cache->psk = psk; + psk = NULL; + } +- os_free(strpassphrase); ++skip: + os_free(psk); ++free_pass: + os_free(passphrase); + } + } +@@ -535,7 +576,10 @@ + cache->acct_interim_interval = 0; + } + +- cache->vlan_id = radius_msg_get_vlanid(msg); ++ if (hapd->conf->ssid.dynamic_vlan != DYNAMIC_VLAN_DISABLED) ++ cache->vlan_id.notempty = !!radius_msg_get_vlanid( ++ msg, &cache->vlan_id.untagged, ++ MAX_NUM_TAGGED_VLAN, cache->vlan_id.tagged); + + decode_tunnel_passwords(hapd, shared_secret, shared_secret_len, + msg, req, cache); +@@ -558,17 +602,18 @@ + !cache->psk) + cache->accepted = HOSTAPD_ACL_REJECT; + +- if (cache->vlan_id && +- !hostapd_vlan_id_valid(hapd->conf->vlan, cache->vlan_id)) { ++ if (cache->vlan_id.notempty && ++ !hostapd_vlan_valid(hapd->conf->vlan, &cache->vlan_id)) { + hostapd_logger(hapd, query->addr, + HOSTAPD_MODULE_RADIUS, + HOSTAPD_LEVEL_INFO, +- "Invalid VLAN ID %d received from RADIUS server", +- cache->vlan_id); +- cache->vlan_id = 0; ++ "Invalid VLAN %d%s received from RADIUS server", ++ cache->vlan_id.untagged, ++ cache->vlan_id.tagged[0] ? "+" : ""); ++ os_memset(&cache->vlan_id, 0, sizeof(cache->vlan_id)); + } + if (hapd->conf->ssid.dynamic_vlan == DYNAMIC_VLAN_REQUIRED && +- !cache->vlan_id) ++ !cache->vlan_id.notempty) + cache->accepted = HOSTAPD_ACL_REJECT; + } else + cache->accepted = HOSTAPD_ACL_REJECT; +@@ -627,9 +672,11 @@ + + #ifndef CONFIG_NO_RADIUS + hostapd_acl_cache_free(hapd->acl_cache); ++ hapd->acl_cache = NULL; + #endif /* CONFIG_NO_RADIUS */ + + query = hapd->acl_queries; ++ hapd->acl_queries = NULL; + while (query) { + prev = query; + query = query->next; +@@ -640,6 +687,12 @@ + + void hostapd_free_psk_list(struct hostapd_sta_wpa_psk_short *psk) + { ++ if (psk && psk->ref) { ++ /* This will be freed when the last reference is dropped. */ ++ psk->ref--; ++ return; ++ } ++ + while (psk) { + struct hostapd_sta_wpa_psk_short *prev = psk; + psk = psk->next; +--- contrib/wpa/src/ap/ieee802_11_auth.h.orig ++++ contrib/wpa/src/ap/ieee802_11_auth.h +@@ -16,11 +16,15 @@ + HOSTAPD_ACL_ACCEPT_TIMEOUT = 3 + }; + ++int hostapd_check_acl(struct hostapd_data *hapd, const u8 *addr, ++ struct vlan_description *vlan_id); + int hostapd_allowed_address(struct hostapd_data *hapd, const u8 *addr, + const u8 *msg, size_t len, u32 *session_timeout, +- u32 *acct_interim_interval, int *vlan_id, ++ u32 *acct_interim_interval, ++ struct vlan_description *vlan_id, + struct hostapd_sta_wpa_psk_short **psk, +- char **identity, char **radius_cui); ++ char **identity, char **radius_cui, ++ int is_probe_req); + int hostapd_acl_init(struct hostapd_data *hapd); + void hostapd_acl_deinit(struct hostapd_data *hapd); + void hostapd_free_psk_list(struct hostapd_sta_wpa_psk_short *psk); +--- contrib/wpa/src/ap/ieee802_11_he.c.orig ++++ contrib/wpa/src/ap/ieee802_11_he.c +@@ -0,0 +1,119 @@ ++/* ++ * hostapd / IEEE 802.11ax HE ++ * Copyright (c) 2016-2017, 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 "hostapd.h" ++#include "ap_config.h" ++#include "beacon.h" ++#include "ieee802_11.h" ++#include "dfs.h" ++ ++u8 * hostapd_eid_he_capab(struct hostapd_data *hapd, u8 *eid) ++{ ++ struct ieee80211_he_capabilities *cap; ++ u8 *pos = eid; ++ ++ if (!hapd->iface->current_mode) ++ return eid; ++ ++ *pos++ = WLAN_EID_EXTENSION; ++ *pos++ = 1 + sizeof(struct ieee80211_he_capabilities); ++ *pos++ = WLAN_EID_EXT_HE_CAPABILITIES; ++ ++ cap = (struct ieee80211_he_capabilities *) pos; ++ os_memset(cap, 0, sizeof(*cap)); ++ ++ if (hapd->iface->conf->he_phy_capab.he_su_beamformer) ++ cap->he_phy_capab_info[HE_PHYCAP_SU_BEAMFORMER_CAPAB_IDX] |= ++ HE_PHYCAP_SU_BEAMFORMER_CAPAB; ++ ++ if (hapd->iface->conf->he_phy_capab.he_su_beamformee) ++ cap->he_phy_capab_info[HE_PHYCAP_SU_BEAMFORMEE_CAPAB_IDX] |= ++ HE_PHYCAP_SU_BEAMFORMEE_CAPAB; ++ ++ if (hapd->iface->conf->he_phy_capab.he_mu_beamformer) ++ cap->he_phy_capab_info[HE_PHYCAP_MU_BEAMFORMER_CAPAB_IDX] |= ++ HE_PHYCAP_MU_BEAMFORMER_CAPAB; ++ ++ pos += sizeof(*cap); ++ ++ return pos; ++} ++ ++ ++u8 * hostapd_eid_he_operation(struct hostapd_data *hapd, u8 *eid) ++{ ++ struct ieee80211_he_operation *oper; ++ u8 *pos = eid; ++ ++ if (!hapd->iface->current_mode) ++ return eid; ++ ++ *pos++ = WLAN_EID_EXTENSION; ++ *pos++ = 1 + sizeof(struct ieee80211_he_operation); ++ *pos++ = WLAN_EID_EXT_HE_OPERATION; ++ ++ oper = (struct ieee80211_he_operation *) pos; ++ os_memset(oper, 0, sizeof(*oper)); ++ ++ if (hapd->iface->conf->he_op.he_bss_color) ++ oper->he_oper_params |= hapd->iface->conf->he_op.he_bss_color; ++ ++ if (hapd->iface->conf->he_op.he_default_pe_duration) ++ oper->he_oper_params |= ++ (hapd->iface->conf->he_op.he_default_pe_duration << ++ HE_OPERATION_DFLT_PE_DURATION_OFFSET); ++ ++ if (hapd->iface->conf->he_op.he_twt_required) ++ oper->he_oper_params |= HE_OPERATION_TWT_REQUIRED; ++ ++ if (hapd->iface->conf->he_op.he_rts_threshold) ++ oper->he_oper_params |= ++ (hapd->iface->conf->he_op.he_rts_threshold << ++ HE_OPERATION_RTS_THRESHOLD_OFFSET); ++ ++ /* TODO: conditional MaxBSSID Indicator subfield */ ++ ++ pos += sizeof(*oper); ++ ++ return pos; ++} ++ ++ ++u8 * hostapd_eid_he_mu_edca_parameter_set(struct hostapd_data *hapd, u8 *eid) ++{ ++ struct ieee80211_he_mu_edca_parameter_set *edca; ++ u8 *pos; ++ size_t i; ++ ++ pos = (u8 *) &hapd->iface->conf->he_mu_edca; ++ for (i = 0; i < sizeof(*edca); i++) { ++ if (pos[i]) ++ break; ++ } ++ if (i == sizeof(*edca)) ++ return eid; /* no MU EDCA Parameters configured */ ++ ++ pos = eid; ++ *pos++ = WLAN_EID_EXTENSION; ++ *pos++ = 1 + sizeof(*edca); ++ *pos++ = WLAN_EID_EXT_HE_MU_EDCA_PARAMS; ++ ++ edca = (struct ieee80211_he_mu_edca_parameter_set *) pos; ++ os_memcpy(edca, &hapd->iface->conf->he_mu_edca, sizeof(*edca)); ++ ++ wpa_hexdump(MSG_DEBUG, "HE: MU EDCA Parameter Set element", ++ pos, sizeof(*edca)); ++ ++ pos += sizeof(*edca); ++ ++ return pos; ++} +--- contrib/wpa/src/ap/ieee802_11_ht.c.orig ++++ contrib/wpa/src/ap/ieee802_11_ht.c +@@ -108,6 +108,29 @@ + } + + ++u8 * hostapd_eid_secondary_channel(struct hostapd_data *hapd, u8 *eid) ++{ ++ u8 sec_ch; ++ ++ if (!hapd->cs_freq_params.channel || ++ !hapd->cs_freq_params.sec_channel_offset) ++ return eid; ++ ++ if (hapd->cs_freq_params.sec_channel_offset == -1) ++ sec_ch = HT_INFO_HT_PARAM_SECONDARY_CHNL_BELOW; ++ else if (hapd->cs_freq_params.sec_channel_offset == 1) ++ sec_ch = HT_INFO_HT_PARAM_SECONDARY_CHNL_ABOVE; ++ else ++ return eid; ++ ++ *eid++ = WLAN_EID_SECONDARY_CHANNEL_OFFSET; ++ *eid++ = 1; ++ *eid++ = sec_ch; ++ ++ return eid; ++} ++ ++ + /* + op_mode + Set to 0 (HT pure) under the followign conditions +@@ -213,17 +236,29 @@ + int i; + const u8 *start = (const u8 *) mgmt; + const u8 *data = start + IEEE80211_HDRLEN + 2; ++ struct sta_info *sta; + ++ wpa_printf(MSG_DEBUG, ++ "HT: Received 20/40 BSS Coexistence Management frame from " ++ MACSTR, MAC2STR(mgmt->sa)); ++ + hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_DEBUG, "hostapd_public_action - action=%d", + mgmt->u.action.u.public_action.action); + +- if (!(iface->conf->ht_capab & HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET)) ++ if (!(iface->conf->ht_capab & HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET)) { ++ wpa_printf(MSG_DEBUG, ++ "Ignore 20/40 BSS Coexistence Management frame since 40 MHz capability is not enabled"); + return; ++ } + +- if (len < IEEE80211_HDRLEN + 2 + sizeof(*bc_ie)) ++ if (len < IEEE80211_HDRLEN + 2 + sizeof(*bc_ie)) { ++ wpa_printf(MSG_DEBUG, ++ "Ignore too short 20/40 BSS Coexistence Management frame"); + return; ++ } + ++ /* 20/40 BSS Coexistence element */ + bc_ie = (struct ieee80211_2040_bss_coex_ie *) data; + if (bc_ie->element_id != WLAN_EID_20_40_BSS_COEXISTENCE || + bc_ie->length < 1) { +@@ -231,13 +266,35 @@ + bc_ie->element_id, bc_ie->length); + return; + } +- if (len < IEEE80211_HDRLEN + 2 + 2 + bc_ie->length) ++ if (len < IEEE80211_HDRLEN + 2 + 2 + bc_ie->length) { ++ wpa_printf(MSG_DEBUG, ++ "Truncated 20/40 BSS Coexistence element"); + return; ++ } + data += 2 + bc_ie->length; + +- wpa_printf(MSG_DEBUG, "20/40 BSS Coexistence Information field: 0x%x", +- bc_ie->coex_param); ++ wpa_printf(MSG_DEBUG, ++ "20/40 BSS Coexistence Information field: 0x%x (%s%s%s%s%s%s)", ++ bc_ie->coex_param, ++ (bc_ie->coex_param & BIT(0)) ? "[InfoReq]" : "", ++ (bc_ie->coex_param & BIT(1)) ? "[40MHzIntolerant]" : "", ++ (bc_ie->coex_param & BIT(2)) ? "[20MHzBSSWidthReq]" : "", ++ (bc_ie->coex_param & BIT(3)) ? "[OBSSScanExemptionReq]" : "", ++ (bc_ie->coex_param & BIT(4)) ? ++ "[OBSSScanExemptionGrant]" : "", ++ (bc_ie->coex_param & (BIT(5) | BIT(6) | BIT(7))) ? ++ "[Reserved]" : ""); ++ + if (bc_ie->coex_param & WLAN_20_40_BSS_COEX_20MHZ_WIDTH_REQ) { ++ /* Intra-BSS communication prohibiting 20/40 MHz BSS operation ++ */ ++ sta = ap_get_sta(hapd, mgmt->sa); ++ if (!sta || !(sta->flags & WLAN_STA_ASSOC)) { ++ wpa_printf(MSG_DEBUG, ++ "Ignore intra-BSS 20/40 BSS Coexistence Management frame from not-associated STA"); ++ return; ++ } ++ + hostapd_logger(hapd, mgmt->sa, + HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_DEBUG, +@@ -246,6 +303,8 @@ + } + + if (bc_ie->coex_param & WLAN_20_40_BSS_COEX_40MHZ_INTOL) { ++ /* Inter-BSS communication prohibiting 20/40 MHz BSS operation ++ */ + hostapd_logger(hapd, mgmt->sa, + HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_DEBUG, +@@ -253,12 +312,16 @@ + is_ht40_allowed = 0; + } + +- if (start + len - data >= 3 && +- data[0] == WLAN_EID_20_40_BSS_INTOLERANT && data[1] >= 1) { ++ /* 20/40 BSS Intolerant Channel Report element (zero or more times) */ ++ while (start + len - data >= 3 && ++ data[0] == WLAN_EID_20_40_BSS_INTOLERANT && data[1] >= 1) { + u8 ielen = data[1]; + +- if (ielen > start + len - data - 2) ++ if (ielen > start + len - data - 2) { ++ wpa_printf(MSG_DEBUG, ++ "Truncated 20/40 BSS Intolerant Channel Report element"); + return; ++ } + ic_report = (struct ieee80211_2040_intol_chan_report *) data; + wpa_printf(MSG_DEBUG, + "20/40 BSS Intolerant Channel Report: Operating Class %u", +@@ -269,8 +332,10 @@ + for (i = 0; i < ielen - 1; i++) { + u8 chan = ic_report->variable[i]; + ++ if (chan == iface->conf->channel) ++ continue; /* matching own primary channel */ + if (is_40_allowed(iface, chan)) +- continue; ++ continue; /* not within affected channels */ + hostapd_logger(hapd, mgmt->sa, + HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_DEBUG, +@@ -278,6 +343,8 @@ + chan); + is_ht40_allowed = 0; + } ++ ++ data += 2 + ielen; + } + wpa_printf(MSG_DEBUG, "is_ht40_allowed=%d num_sta_ht40_intolerant=%d", + is_ht40_allowed, iface->num_sta_ht40_intolerant); +@@ -317,8 +384,8 @@ + * that did not specify a valid WMM IE in the (Re)Association Request + * frame. + */ +- if (!ht_capab || +- !(sta->flags & WLAN_STA_WMM) || hapd->conf->disable_11n) { ++ if (!ht_capab || !(sta->flags & WLAN_STA_WMM) || ++ !hapd->iconf->ieee80211n || hapd->conf->disable_11n) { + sta->flags &= ~WLAN_STA_HT; + os_free(sta->ht_capabilities); + sta->ht_capabilities = NULL; +--- contrib/wpa/src/ap/ieee802_11_shared.c.orig ++++ contrib/wpa/src/ap/ieee802_11_shared.c +@@ -10,10 +10,12 @@ + + #include "utils/common.h" + #include "common/ieee802_11_defs.h" ++#include "common/ocv.h" + #include "hostapd.h" + #include "sta_info.h" + #include "ap_config.h" + #include "ap_drv_ops.h" ++#include "wpa_auth.h" + #include "ieee802_11.h" + + +@@ -49,7 +51,12 @@ + void ieee802_11_send_sa_query_req(struct hostapd_data *hapd, + const u8 *addr, const u8 *trans_id) + { +- struct ieee80211_mgmt mgmt; ++#ifdef CONFIG_OCV ++ struct sta_info *sta; ++#endif /* CONFIG_OCV */ ++ struct ieee80211_mgmt *mgmt; ++ u8 *oci_ie = NULL; ++ u8 oci_ie_len = 0; + u8 *end; + + wpa_printf(MSG_DEBUG, "IEEE 802.11: Sending SA Query Request to " +@@ -57,19 +64,61 @@ + wpa_hexdump(MSG_DEBUG, "IEEE 802.11: SA Query Transaction ID", + trans_id, WLAN_SA_QUERY_TR_ID_LEN); + +- os_memset(&mgmt, 0, sizeof(mgmt)); +- mgmt.frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, +- WLAN_FC_STYPE_ACTION); +- os_memcpy(mgmt.da, addr, ETH_ALEN); +- os_memcpy(mgmt.sa, hapd->own_addr, ETH_ALEN); +- os_memcpy(mgmt.bssid, hapd->own_addr, ETH_ALEN); +- mgmt.u.action.category = WLAN_ACTION_SA_QUERY; +- mgmt.u.action.u.sa_query_req.action = WLAN_SA_QUERY_REQUEST; +- os_memcpy(mgmt.u.action.u.sa_query_req.trans_id, trans_id, ++#ifdef CONFIG_OCV ++ sta = ap_get_sta(hapd, addr); ++ if (sta && wpa_auth_uses_ocv(sta->wpa_sm)) { ++ struct wpa_channel_info ci; ++ ++ if (hostapd_drv_channel_info(hapd, &ci) != 0) { ++ wpa_printf(MSG_WARNING, ++ "Failed to get channel info for OCI element in SA Query Request"); ++ return; ++ } ++ ++ oci_ie_len = OCV_OCI_EXTENDED_LEN; ++ oci_ie = os_zalloc(oci_ie_len); ++ if (!oci_ie) { ++ wpa_printf(MSG_WARNING, ++ "Failed to allocate buffer for OCI element in SA Query Request"); ++ return; ++ } ++ ++ if (ocv_insert_extended_oci(&ci, oci_ie) < 0) { ++ os_free(oci_ie); ++ return; ++ } ++ } ++#endif /* CONFIG_OCV */ ++ ++ mgmt = os_zalloc(sizeof(*mgmt) + oci_ie_len); ++ if (!mgmt) { ++ wpa_printf(MSG_DEBUG, ++ "Failed to allocate buffer for SA Query Response frame"); ++ os_free(oci_ie); ++ return; ++ } ++ ++ mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, ++ WLAN_FC_STYPE_ACTION); ++ os_memcpy(mgmt->da, addr, ETH_ALEN); ++ os_memcpy(mgmt->sa, hapd->own_addr, ETH_ALEN); ++ os_memcpy(mgmt->bssid, hapd->own_addr, ETH_ALEN); ++ mgmt->u.action.category = WLAN_ACTION_SA_QUERY; ++ mgmt->u.action.u.sa_query_req.action = WLAN_SA_QUERY_REQUEST; ++ os_memcpy(mgmt->u.action.u.sa_query_req.trans_id, trans_id, + WLAN_SA_QUERY_TR_ID_LEN); +- end = mgmt.u.action.u.sa_query_req.trans_id + WLAN_SA_QUERY_TR_ID_LEN; +- if (hostapd_drv_send_mlme(hapd, &mgmt, end - (u8 *) &mgmt, 0) < 0) ++ end = mgmt->u.action.u.sa_query_req.variable; ++#ifdef CONFIG_OCV ++ if (oci_ie_len > 0) { ++ os_memcpy(end, oci_ie, oci_ie_len); ++ end += oci_ie_len; ++ } ++#endif /* CONFIG_OCV */ ++ if (hostapd_drv_send_mlme(hapd, mgmt, end - (u8 *) mgmt, 0) < 0) + wpa_printf(MSG_INFO, "ieee802_11_send_sa_query_req: send failed"); ++ ++ os_free(mgmt); ++ os_free(oci_ie); + } + + +@@ -77,7 +126,9 @@ + const u8 *sa, const u8 *trans_id) + { + struct sta_info *sta; +- struct ieee80211_mgmt resp; ++ struct ieee80211_mgmt *resp; ++ u8 *oci_ie = NULL; ++ u8 oci_ie_len = 0; + u8 *end; + + wpa_printf(MSG_DEBUG, "IEEE 802.11: Received SA Query Request from " +@@ -92,31 +143,124 @@ + return; + } + ++#ifdef CONFIG_OCV ++ if (wpa_auth_uses_ocv(sta->wpa_sm)) { ++ struct wpa_channel_info ci; ++ ++ if (hostapd_drv_channel_info(hapd, &ci) != 0) { ++ wpa_printf(MSG_WARNING, ++ "Failed to get channel info for OCI element in SA Query Response"); ++ return; ++ } ++ ++ oci_ie_len = OCV_OCI_EXTENDED_LEN; ++ oci_ie = os_zalloc(oci_ie_len); ++ if (!oci_ie) { ++ wpa_printf(MSG_WARNING, ++ "Failed to allocate buffer for for OCI element in SA Query Response"); ++ return; ++ } ++ ++ if (ocv_insert_extended_oci(&ci, oci_ie) < 0) { ++ os_free(oci_ie); ++ return; ++ } ++ } ++#endif /* CONFIG_OCV */ ++ ++ resp = os_zalloc(sizeof(*resp) + oci_ie_len); ++ if (!resp) { ++ wpa_printf(MSG_DEBUG, ++ "Failed to allocate buffer for SA Query Response frame"); ++ os_free(oci_ie); ++ return; ++ } ++ + wpa_printf(MSG_DEBUG, "IEEE 802.11: Sending SA Query Response to " + MACSTR, MAC2STR(sa)); + +- os_memset(&resp, 0, sizeof(resp)); +- resp.frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, +- WLAN_FC_STYPE_ACTION); +- os_memcpy(resp.da, sa, ETH_ALEN); +- os_memcpy(resp.sa, hapd->own_addr, ETH_ALEN); +- os_memcpy(resp.bssid, hapd->own_addr, ETH_ALEN); +- resp.u.action.category = WLAN_ACTION_SA_QUERY; +- resp.u.action.u.sa_query_req.action = WLAN_SA_QUERY_RESPONSE; +- os_memcpy(resp.u.action.u.sa_query_req.trans_id, trans_id, ++ resp->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, ++ WLAN_FC_STYPE_ACTION); ++ os_memcpy(resp->da, sa, ETH_ALEN); ++ os_memcpy(resp->sa, hapd->own_addr, ETH_ALEN); ++ os_memcpy(resp->bssid, hapd->own_addr, ETH_ALEN); ++ resp->u.action.category = WLAN_ACTION_SA_QUERY; ++ resp->u.action.u.sa_query_req.action = WLAN_SA_QUERY_RESPONSE; ++ os_memcpy(resp->u.action.u.sa_query_req.trans_id, trans_id, + WLAN_SA_QUERY_TR_ID_LEN); +- end = resp.u.action.u.sa_query_req.trans_id + WLAN_SA_QUERY_TR_ID_LEN; +- if (hostapd_drv_send_mlme(hapd, &resp, end - (u8 *) &resp, 0) < 0) ++ end = resp->u.action.u.sa_query_req.variable; ++#ifdef CONFIG_OCV ++ if (oci_ie_len > 0) { ++ os_memcpy(end, oci_ie, oci_ie_len); ++ end += oci_ie_len; ++ } ++#endif /* CONFIG_OCV */ ++ if (hostapd_drv_send_mlme(hapd, resp, end - (u8 *) resp, 0) < 0) + wpa_printf(MSG_INFO, "ieee80211_mgmt_sa_query_request: send failed"); ++ ++ os_free(resp); ++ os_free(oci_ie); + } + + +-void ieee802_11_sa_query_action(struct hostapd_data *hapd, const u8 *sa, +- const u8 action_type, const u8 *trans_id) ++void ieee802_11_sa_query_action(struct hostapd_data *hapd, ++ const struct ieee80211_mgmt *mgmt, ++ size_t len) + { + struct sta_info *sta; + int i; ++ const u8 *sa = mgmt->sa; ++ const u8 action_type = mgmt->u.action.u.sa_query_resp.action; ++ const u8 *trans_id = mgmt->u.action.u.sa_query_resp.trans_id; + ++ if (((const u8 *) mgmt) + len < ++ mgmt->u.action.u.sa_query_resp.variable) { ++ wpa_printf(MSG_DEBUG, ++ "IEEE 802.11: Too short SA Query Action frame (len=%lu)", ++ (unsigned long) len); ++ return; ++ } ++ ++ sta = ap_get_sta(hapd, sa); ++ ++#ifdef CONFIG_OCV ++ if (sta && wpa_auth_uses_ocv(sta->wpa_sm)) { ++ struct ieee802_11_elems elems; ++ struct wpa_channel_info ci; ++ int tx_chanwidth; ++ int tx_seg1_idx; ++ size_t ies_len; ++ const u8 *ies; ++ ++ ies = mgmt->u.action.u.sa_query_resp.variable; ++ ies_len = len - (ies - (u8 *) mgmt); ++ if (ieee802_11_parse_elems(ies, ies_len, &elems, 1) == ++ ParseFailed) { ++ wpa_printf(MSG_DEBUG, ++ "SA Query: Failed to parse elements"); ++ return; ++ } ++ ++ if (hostapd_drv_channel_info(hapd, &ci) != 0) { ++ wpa_printf(MSG_WARNING, ++ "Failed to get channel info to validate received OCI in SA Query Action frame"); ++ return; ++ } ++ ++ if (get_sta_tx_parameters(sta->wpa_sm, ++ channel_width_to_int(ci.chanwidth), ++ ci.seg1_idx, &tx_chanwidth, ++ &tx_seg1_idx) < 0) ++ return; ++ ++ if (ocv_verify_tx_params(elems.oci, elems.oci_len, &ci, ++ tx_chanwidth, tx_seg1_idx) != 0) { ++ wpa_printf(MSG_WARNING, "%s", ocv_errorstr); ++ return; ++ } ++ } ++#endif /* CONFIG_OCV */ ++ + if (action_type == WLAN_SA_QUERY_REQUEST) { + ieee802_11_send_sa_query_resp(hapd, sa, trans_id); + return; +@@ -135,7 +279,6 @@ + + /* MLME-SAQuery.confirm */ + +- sta = ap_get_sta(hapd, sa); + if (sta == NULL || sta->sa_query_trans_id == NULL) { + wpa_printf(MSG_DEBUG, "IEEE 802.11: No matching STA with " + "pending SA Query request found"); +@@ -172,10 +315,16 @@ + case 0: /* Bits 0-7 */ + if (hapd->iconf->obss_interval) + *pos |= 0x01; /* Bit 0 - Coexistence management */ ++ if (hapd->iface->drv_flags & WPA_DRIVER_FLAGS_AP_CSA) ++ *pos |= 0x04; /* Bit 2 - Extended Channel Switching */ + break; + case 1: /* Bits 8-15 */ + if (hapd->conf->proxy_arp) + *pos |= 0x10; /* Bit 12 - Proxy ARP */ ++ if (hapd->conf->coloc_intf_reporting) { ++ /* Bit 13 - Collocated Interference Reporting */ ++ *pos |= 0x20; ++ } + break; + case 2: /* Bits 16-23 */ + if (hapd->conf->wnm_sleep_mode) +@@ -184,9 +333,9 @@ + *pos |= 0x08; /* Bit 19 - BSS Transition */ + break; + case 3: /* Bits 24-31 */ +-#ifdef CONFIG_WNM ++#ifdef CONFIG_WNM_AP + *pos |= 0x02; /* Bit 25 - SSID List */ +-#endif /* CONFIG_WNM */ ++#endif /* CONFIG_WNM_AP */ + if (hapd->conf->time_advertisement == 2) + *pos |= 0x08; /* Bit 27 - UTC TSF Offset */ + if (hapd->conf->interworking) +@@ -207,11 +356,45 @@ + if (hapd->conf->hs20) + *pos |= 0x40; /* Bit 46 - WNM-Notification */ + #endif /* CONFIG_HS20 */ ++#ifdef CONFIG_MBO ++ if (hapd->conf->mbo_enabled) ++ *pos |= 0x40; /* Bit 46 - WNM-Notification */ ++#endif /* CONFIG_MBO */ + break; + case 6: /* Bits 48-55 */ + if (hapd->conf->ssid.utf8_ssid) + *pos |= 0x01; /* Bit 48 - UTF-8 SSID */ + break; ++ case 7: /* Bits 56-63 */ ++ break; ++ case 8: /* Bits 64-71 */ ++ if (hapd->conf->ftm_responder) ++ *pos |= 0x40; /* Bit 70 - FTM responder */ ++ if (hapd->conf->ftm_initiator) ++ *pos |= 0x80; /* Bit 71 - FTM initiator */ ++ break; ++ case 9: /* Bits 72-79 */ ++#ifdef CONFIG_FILS ++ if ((hapd->conf->wpa & WPA_PROTO_RSN) && ++ wpa_key_mgmt_fils(hapd->conf->wpa_key_mgmt)) ++ *pos |= 0x01; ++#endif /* CONFIG_FILS */ ++ break; ++ case 10: /* Bits 80-87 */ ++#ifdef CONFIG_SAE ++ if (hapd->conf->wpa && ++ wpa_key_mgmt_sae(hapd->conf->wpa_key_mgmt)) { ++ int in_use = hostapd_sae_pw_id_in_use(hapd->conf); ++ ++ if (in_use) ++ *pos |= 0x02; /* Bit 81 - SAE Password ++ * Identifiers In Use */ ++ if (in_use == 2) ++ *pos |= 0x04; /* Bit 82 - SAE Password ++ * Identifiers Used Exclusively */ ++ } ++#endif /* CONFIG_SAE */ ++ break; + } + } + +@@ -231,14 +414,32 @@ + len = 1; + if (len < 7 && hapd->conf->ssid.utf8_ssid) + len = 7; +-#ifdef CONFIG_WNM ++ if (len < 9 && ++ (hapd->conf->ftm_initiator || hapd->conf->ftm_responder)) ++ len = 9; ++#ifdef CONFIG_WNM_AP + if (len < 4) + len = 4; +-#endif /* CONFIG_WNM */ ++#endif /* CONFIG_WNM_AP */ + #ifdef CONFIG_HS20 + if (hapd->conf->hs20 && len < 6) + len = 6; + #endif /* CONFIG_HS20 */ ++#ifdef CONFIG_MBO ++ if (hapd->conf->mbo_enabled && len < 6) ++ len = 6; ++#endif /* CONFIG_MBO */ ++#ifdef CONFIG_FILS ++ if ((!(hapd->conf->wpa & WPA_PROTO_RSN) || ++ !wpa_key_mgmt_fils(hapd->conf->wpa_key_mgmt)) && len < 10) ++ len = 10; ++#endif /* CONFIG_FILS */ ++#ifdef CONFIG_SAE ++ if (len < 11 && hapd->conf->wpa && ++ wpa_key_mgmt_sae(hapd->conf->wpa_key_mgmt) && ++ hostapd_sae_pw_id_in_use(hapd->conf)) ++ len = 11; ++#endif /* CONFIG_SAE */ + if (len < hapd->iface->extended_capa_len) + len = hapd->iface->extended_capa_len; + if (len == 0) +@@ -413,7 +614,7 @@ + { + size_t len; + +- if (hapd->conf->time_advertisement != 2) ++ if (hapd->conf->time_advertisement != 2 || !hapd->conf->time_zone) + return eid; + + len = os_strlen(hapd->conf->time_zone); +@@ -484,7 +685,7 @@ + { + u8 *pos = eid; + +-#ifdef CONFIG_WNM ++#ifdef CONFIG_WNM_AP + if (hapd->conf->ap_max_inactivity > 0) { + unsigned int val; + *pos++ = WLAN_EID_BSS_MAX_IDLE_PERIOD; +@@ -502,7 +703,300 @@ + pos += 2; + *pos++ = 0x00; /* TODO: Protected Keep-Alive Required */ + } +-#endif /* CONFIG_WNM */ ++#endif /* CONFIG_WNM_AP */ + + return pos; + } ++ ++ ++#ifdef CONFIG_MBO ++ ++u8 * hostapd_eid_mbo_rssi_assoc_rej(struct hostapd_data *hapd, u8 *eid, ++ size_t len, int delta) ++{ ++ u8 mbo[4]; ++ ++ mbo[0] = OCE_ATTR_ID_RSSI_BASED_ASSOC_REJECT; ++ mbo[1] = 2; ++ /* Delta RSSI */ ++ mbo[2] = delta; ++ /* Retry delay */ ++ mbo[3] = hapd->iconf->rssi_reject_assoc_timeout; ++ ++ return eid + mbo_add_ie(eid, len, mbo, 4); ++} ++ ++ ++u8 * hostapd_eid_mbo(struct hostapd_data *hapd, u8 *eid, size_t len) ++{ ++ u8 mbo[9], *mbo_pos = mbo; ++ u8 *pos = eid; ++ ++ if (!hapd->conf->mbo_enabled && ++ !OCE_STA_CFON_ENABLED(hapd) && !OCE_AP_ENABLED(hapd)) ++ return eid; ++ ++ if (hapd->conf->mbo_enabled) { ++ *mbo_pos++ = MBO_ATTR_ID_AP_CAPA_IND; ++ *mbo_pos++ = 1; ++ /* Not Cellular aware */ ++ *mbo_pos++ = 0; ++ } ++ ++ if (hapd->conf->mbo_enabled && hapd->mbo_assoc_disallow) { ++ *mbo_pos++ = MBO_ATTR_ID_ASSOC_DISALLOW; ++ *mbo_pos++ = 1; ++ *mbo_pos++ = hapd->mbo_assoc_disallow; ++ } ++ ++ if (OCE_STA_CFON_ENABLED(hapd) || OCE_AP_ENABLED(hapd)) { ++ u8 ctrl; ++ ++ ctrl = OCE_RELEASE; ++ if (OCE_STA_CFON_ENABLED(hapd) && !OCE_AP_ENABLED(hapd)) ++ ctrl |= OCE_IS_STA_CFON; ++ ++ *mbo_pos++ = OCE_ATTR_ID_CAPA_IND; ++ *mbo_pos++ = 1; ++ *mbo_pos++ = ctrl; ++ } ++ ++ pos += mbo_add_ie(pos, len, mbo, mbo_pos - mbo); ++ ++ return pos; ++} ++ ++ ++u8 hostapd_mbo_ie_len(struct hostapd_data *hapd) ++{ ++ u8 len; ++ ++ if (!hapd->conf->mbo_enabled && ++ !OCE_STA_CFON_ENABLED(hapd) && !OCE_AP_ENABLED(hapd)) ++ return 0; ++ ++ /* ++ * MBO IE header (6) + Capability Indication attribute (3) + ++ * Association Disallowed attribute (3) = 12 ++ */ ++ len = 6; ++ if (hapd->conf->mbo_enabled) ++ len += 3 + (hapd->mbo_assoc_disallow ? 3 : 0); ++ ++ /* OCE capability indication attribute (3) */ ++ if (OCE_STA_CFON_ENABLED(hapd) || OCE_AP_ENABLED(hapd)) ++ len += 3; ++ ++ return len; ++} ++ ++#endif /* CONFIG_MBO */ ++ ++ ++#ifdef CONFIG_OWE ++static int hostapd_eid_owe_trans_enabled(struct hostapd_data *hapd) ++{ ++ return hapd->conf->owe_transition_ssid_len > 0 && ++ !is_zero_ether_addr(hapd->conf->owe_transition_bssid); ++} ++#endif /* CONFIG_OWE */ ++ ++ ++size_t hostapd_eid_owe_trans_len(struct hostapd_data *hapd) ++{ ++#ifdef CONFIG_OWE ++ if (!hostapd_eid_owe_trans_enabled(hapd)) ++ return 0; ++ return 6 + ETH_ALEN + 1 + hapd->conf->owe_transition_ssid_len; ++#else /* CONFIG_OWE */ ++ return 0; ++#endif /* CONFIG_OWE */ ++} ++ ++ ++u8 * hostapd_eid_owe_trans(struct hostapd_data *hapd, u8 *eid, ++ size_t len) ++{ ++#ifdef CONFIG_OWE ++ u8 *pos = eid; ++ size_t elen; ++ ++ if (hapd->conf->owe_transition_ifname[0] && ++ !hostapd_eid_owe_trans_enabled(hapd)) ++ hostapd_owe_trans_get_info(hapd); ++ ++ if (!hostapd_eid_owe_trans_enabled(hapd)) ++ return pos; ++ ++ elen = hostapd_eid_owe_trans_len(hapd); ++ if (len < elen) { ++ wpa_printf(MSG_DEBUG, ++ "OWE: Not enough room in the buffer for OWE IE"); ++ return pos; ++ } ++ ++ *pos++ = WLAN_EID_VENDOR_SPECIFIC; ++ *pos++ = elen - 2; ++ WPA_PUT_BE24(pos, OUI_WFA); ++ pos += 3; ++ *pos++ = OWE_OUI_TYPE; ++ os_memcpy(pos, hapd->conf->owe_transition_bssid, ETH_ALEN); ++ pos += ETH_ALEN; ++ *pos++ = hapd->conf->owe_transition_ssid_len; ++ os_memcpy(pos, hapd->conf->owe_transition_ssid, ++ hapd->conf->owe_transition_ssid_len); ++ pos += hapd->conf->owe_transition_ssid_len; ++ ++ return pos; ++#else /* CONFIG_OWE */ ++ return eid; ++#endif /* CONFIG_OWE */ ++} ++ ++ ++void ap_copy_sta_supp_op_classes(struct sta_info *sta, ++ const u8 *supp_op_classes, ++ size_t supp_op_classes_len) ++{ ++ if (!supp_op_classes) ++ return; ++ os_free(sta->supp_op_classes); ++ sta->supp_op_classes = os_malloc(1 + supp_op_classes_len); ++ if (!sta->supp_op_classes) ++ return; ++ ++ sta->supp_op_classes[0] = supp_op_classes_len; ++ os_memcpy(sta->supp_op_classes + 1, supp_op_classes, ++ supp_op_classes_len); ++} ++ ++ ++u8 * hostapd_eid_fils_indic(struct hostapd_data *hapd, u8 *eid, int hessid) ++{ ++ u8 *pos = eid; ++#ifdef CONFIG_FILS ++ u8 *len; ++ u16 fils_info = 0; ++ size_t realms; ++ struct fils_realm *realm; ++ ++ if (!(hapd->conf->wpa & WPA_PROTO_RSN) || ++ !wpa_key_mgmt_fils(hapd->conf->wpa_key_mgmt)) ++ return pos; ++ ++ realms = dl_list_len(&hapd->conf->fils_realms); ++ if (realms > 7) ++ realms = 7; /* 3 bit count field limits this to max 7 */ ++ ++ *pos++ = WLAN_EID_FILS_INDICATION; ++ len = pos++; ++ /* TODO: B0..B2: Number of Public Key Identifiers */ ++ if (hapd->conf->erp_domain) { ++ /* B3..B5: Number of Realm Identifiers */ ++ fils_info |= realms << 3; ++ } ++ /* TODO: B6: FILS IP Address Configuration */ ++ if (hapd->conf->fils_cache_id_set) ++ fils_info |= BIT(7); ++ if (hessid && !is_zero_ether_addr(hapd->conf->hessid)) ++ fils_info |= BIT(8); /* HESSID Included */ ++ /* FILS Shared Key Authentication without PFS Supported */ ++ fils_info |= BIT(9); ++ if (hapd->conf->fils_dh_group) { ++ /* FILS Shared Key Authentication with PFS Supported */ ++ fils_info |= BIT(10); ++ } ++ /* TODO: B11: FILS Public Key Authentication Supported */ ++ /* B12..B15: Reserved */ ++ WPA_PUT_LE16(pos, fils_info); ++ pos += 2; ++ if (hapd->conf->fils_cache_id_set) { ++ os_memcpy(pos, hapd->conf->fils_cache_id, FILS_CACHE_ID_LEN); ++ pos += FILS_CACHE_ID_LEN; ++ } ++ if (hessid && !is_zero_ether_addr(hapd->conf->hessid)) { ++ os_memcpy(pos, hapd->conf->hessid, ETH_ALEN); ++ pos += ETH_ALEN; ++ } ++ ++ dl_list_for_each(realm, &hapd->conf->fils_realms, struct fils_realm, ++ list) { ++ if (realms == 0) ++ break; ++ realms--; ++ os_memcpy(pos, realm->hash, 2); ++ pos += 2; ++ } ++ *len = pos - len - 1; ++#endif /* CONFIG_FILS */ ++ ++ return pos; ++} ++ ++ ++#ifdef CONFIG_OCV ++int get_tx_parameters(struct sta_info *sta, int ap_max_chanwidth, ++ int ap_seg1_idx, int *bandwidth, int *seg1_idx) ++{ ++ int ht_40mhz = 0; ++ int vht_80p80 = 0; ++ int requested_bw; ++ ++ if (sta->ht_capabilities) ++ ht_40mhz = !!(sta->ht_capabilities->ht_capabilities_info & ++ HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET); ++ ++ if (sta->vht_operation) { ++ struct ieee80211_vht_operation *oper = sta->vht_operation; ++ ++ /* ++ * If a VHT Operation element was present, use it to determine ++ * the supported channel bandwidth. ++ */ ++ if (oper->vht_op_info_chwidth == 0) { ++ requested_bw = ht_40mhz ? 40 : 20; ++ } else if (oper->vht_op_info_chan_center_freq_seg1_idx == 0) { ++ requested_bw = 80; ++ } else { ++ int diff; ++ ++ requested_bw = 160; ++ diff = abs((int) ++ oper->vht_op_info_chan_center_freq_seg0_idx - ++ (int) ++ oper->vht_op_info_chan_center_freq_seg1_idx); ++ vht_80p80 = oper->vht_op_info_chan_center_freq_seg1_idx ++ != 0 && diff > 16; ++ } ++ } else if (sta->vht_capabilities) { ++ struct ieee80211_vht_capabilities *capab; ++ int vht_chanwidth; ++ ++ capab = sta->vht_capabilities; ++ ++ /* ++ * If only the VHT Capabilities element is present (e.g., for ++ * normal clients), use it to determine the supported channel ++ * bandwidth. ++ */ ++ vht_chanwidth = capab->vht_capabilities_info & ++ VHT_CAP_SUPP_CHAN_WIDTH_MASK; ++ vht_80p80 = capab->vht_capabilities_info & ++ VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ; ++ ++ /* TODO: Also take into account Extended NSS BW Support field */ ++ requested_bw = vht_chanwidth ? 160 : 80; ++ } else { ++ requested_bw = ht_40mhz ? 40 : 20; ++ } ++ ++ *bandwidth = requested_bw < ap_max_chanwidth ? ++ requested_bw : ap_max_chanwidth; ++ ++ *seg1_idx = 0; ++ if (ap_seg1_idx && vht_80p80) ++ *seg1_idx = ap_seg1_idx; ++ ++ return 0; ++} ++#endif /* CONFIG_OCV */ +--- contrib/wpa/src/ap/ieee802_11_vht.c.orig ++++ contrib/wpa/src/ap/ieee802_11_vht.c +@@ -17,9 +17,10 @@ + #include "sta_info.h" + #include "beacon.h" + #include "ieee802_11.h" ++#include "dfs.h" + + +-u8 * hostapd_eid_vht_capabilities(struct hostapd_data *hapd, u8 *eid) ++u8 * hostapd_eid_vht_capabilities(struct hostapd_data *hapd, u8 *eid, u32 nsts) + { + struct ieee80211_vht_capabilities *cap; + struct hostapd_hw_modes *mode = hapd->iface->current_mode; +@@ -49,6 +50,18 @@ + cap->vht_capabilities_info = host_to_le32( + hapd->iface->conf->vht_capab); + ++ if (nsts != 0) { ++ u32 hapd_nsts; ++ ++ hapd_nsts = le_to_host32(cap->vht_capabilities_info); ++ hapd_nsts = (hapd_nsts >> VHT_CAP_BEAMFORMEE_STS_OFFSET) & 7; ++ cap->vht_capabilities_info &= ++ ~(host_to_le32(hapd_nsts << ++ VHT_CAP_BEAMFORMEE_STS_OFFSET)); ++ cap->vht_capabilities_info |= ++ host_to_le32(nsts << VHT_CAP_BEAMFORMEE_STS_OFFSET); ++ } ++ + /* Supported MCS set comes from hw */ + os_memcpy(&cap->vht_supported_mcs_set, mode->vht_mcs_set, 8); + +@@ -80,6 +93,26 @@ + hapd->iconf->vht_oper_centr_freq_seg1_idx; + + oper->vht_op_info_chwidth = hapd->iconf->vht_oper_chwidth; ++ if (hapd->iconf->vht_oper_chwidth == 2) { ++ /* ++ * Convert 160 MHz channel width to new style as interop ++ * workaround. ++ */ ++ oper->vht_op_info_chwidth = 1; ++ oper->vht_op_info_chan_center_freq_seg1_idx = ++ oper->vht_op_info_chan_center_freq_seg0_idx; ++ if (hapd->iconf->channel < ++ hapd->iconf->vht_oper_centr_freq_seg0_idx) ++ oper->vht_op_info_chan_center_freq_seg0_idx -= 8; ++ else ++ oper->vht_op_info_chan_center_freq_seg0_idx += 8; ++ } else if (hapd->iconf->vht_oper_chwidth == 3) { ++ /* ++ * Convert 80+80 MHz channel width to new style as interop ++ * workaround. ++ */ ++ oper->vht_op_info_chwidth = 1; ++ } + + /* VHT Basic MCS set comes from hw */ + /* Hard code 1 stream, MCS0-7 is a min Basic VHT MCS rates */ +@@ -131,12 +164,177 @@ + } + + ++u8 * hostapd_eid_wb_chsw_wrapper(struct hostapd_data *hapd, u8 *eid) ++{ ++ u8 bw, chan1, chan2 = 0; ++ int freq1; ++ ++ if (!hapd->cs_freq_params.channel || ++ !hapd->cs_freq_params.vht_enabled) ++ return eid; ++ ++ /* bandwidth: 0: 40, 1: 80, 2: 160, 3: 80+80 */ ++ switch (hapd->cs_freq_params.bandwidth) { ++ case 40: ++ bw = 0; ++ break; ++ case 80: ++ /* check if it's 80+80 */ ++ if (!hapd->cs_freq_params.center_freq2) ++ bw = 1; ++ else ++ bw = 3; ++ break; ++ case 160: ++ bw = 2; ++ break; ++ default: ++ /* not valid VHT bandwidth or not in CSA */ ++ return eid; ++ } ++ ++ freq1 = hapd->cs_freq_params.center_freq1 ? ++ hapd->cs_freq_params.center_freq1 : ++ hapd->cs_freq_params.freq; ++ if (ieee80211_freq_to_chan(freq1, &chan1) != ++ HOSTAPD_MODE_IEEE80211A) ++ return eid; ++ ++ if (hapd->cs_freq_params.center_freq2 && ++ ieee80211_freq_to_chan(hapd->cs_freq_params.center_freq2, ++ &chan2) != HOSTAPD_MODE_IEEE80211A) ++ return eid; ++ ++ *eid++ = WLAN_EID_VHT_CHANNEL_SWITCH_WRAPPER; ++ *eid++ = 5; /* Length of Channel Switch Wrapper */ ++ *eid++ = WLAN_EID_VHT_WIDE_BW_CHSWITCH; ++ *eid++ = 3; /* Length of Wide Bandwidth Channel Switch element */ ++ *eid++ = bw; /* New Channel Width */ ++ *eid++ = chan1; /* New Channel Center Frequency Segment 0 */ ++ *eid++ = chan2; /* New Channel Center Frequency Segment 1 */ ++ ++ return eid; ++} ++ ++ ++u8 * hostapd_eid_txpower_envelope(struct hostapd_data *hapd, u8 *eid) ++{ ++ struct hostapd_iface *iface = hapd->iface; ++ struct hostapd_config *iconf = iface->conf; ++ struct hostapd_hw_modes *mode = iface->current_mode; ++ struct hostapd_channel_data *chan; ++ int dfs, i; ++ u8 channel, tx_pwr_count, local_pwr_constraint; ++ int max_tx_power; ++ u8 tx_pwr; ++ ++ if (!mode) ++ return eid; ++ ++ if (ieee80211_freq_to_chan(iface->freq, &channel) == NUM_HOSTAPD_MODES) ++ return eid; ++ ++ for (i = 0; i < mode->num_channels; i++) { ++ if (mode->channels[i].freq == iface->freq) ++ break; ++ } ++ if (i == mode->num_channels) ++ return eid; ++ ++ switch (iface->conf->vht_oper_chwidth) { ++ case VHT_CHANWIDTH_USE_HT: ++ if (iconf->secondary_channel == 0) { ++ /* Max Transmit Power count = 0 (20 MHz) */ ++ tx_pwr_count = 0; ++ } else { ++ /* Max Transmit Power count = 1 (20, 40 MHz) */ ++ tx_pwr_count = 1; ++ } ++ break; ++ case VHT_CHANWIDTH_80MHZ: ++ /* Max Transmit Power count = 2 (20, 40, and 80 MHz) */ ++ tx_pwr_count = 2; ++ break; ++ case VHT_CHANWIDTH_80P80MHZ: ++ case VHT_CHANWIDTH_160MHZ: ++ /* Max Transmit Power count = 3 (20, 40, 80, 160/80+80 MHz) */ ++ tx_pwr_count = 3; ++ break; ++ default: ++ return eid; ++ } ++ ++ /* ++ * Below local_pwr_constraint logic is referred from ++ * hostapd_eid_pwr_constraint. ++ * ++ * Check if DFS is required by regulatory. ++ */ ++ dfs = hostapd_is_dfs_required(hapd->iface); ++ if (dfs < 0) ++ dfs = 0; ++ ++ /* ++ * In order to meet regulations when TPC is not implemented using ++ * a transmit power that is below the legal maximum (including any ++ * mitigation factor) should help. In this case, indicate 3 dB below ++ * maximum allowed transmit power. ++ */ ++ if (hapd->iconf->local_pwr_constraint == -1) ++ local_pwr_constraint = (dfs == 0) ? 0 : 3; ++ else ++ local_pwr_constraint = hapd->iconf->local_pwr_constraint; ++ ++ /* ++ * A STA that is not an AP shall use a transmit power less than or ++ * equal to the local maximum transmit power level for the channel. ++ * The local maximum transmit power can be calculated from the formula: ++ * local max TX pwr = max TX pwr - local pwr constraint ++ * Where max TX pwr is maximum transmit power level specified for ++ * channel in Country element and local pwr constraint is specified ++ * for channel in this Power Constraint element. ++ */ ++ chan = &mode->channels[i]; ++ max_tx_power = chan->max_tx_power - local_pwr_constraint; ++ ++ /* ++ * Local Maximum Transmit power is encoded as two's complement ++ * with a 0.5 dB step. ++ */ ++ max_tx_power *= 2; /* in 0.5 dB steps */ ++ if (max_tx_power > 127) { ++ /* 63.5 has special meaning of 63.5 dBm or higher */ ++ max_tx_power = 127; ++ } ++ if (max_tx_power < -128) ++ max_tx_power = -128; ++ if (max_tx_power < 0) ++ tx_pwr = 0x80 + max_tx_power + 128; ++ else ++ tx_pwr = max_tx_power; ++ ++ *eid++ = WLAN_EID_VHT_TRANSMIT_POWER_ENVELOPE; ++ *eid++ = 2 + tx_pwr_count; ++ ++ /* ++ * Max Transmit Power count and ++ * Max Transmit Power units = 0 (EIRP) ++ */ ++ *eid++ = tx_pwr_count; ++ ++ for (i = 0; i <= tx_pwr_count; i++) ++ *eid++ = tx_pwr; ++ ++ return eid; ++} ++ ++ + u16 copy_sta_vht_capab(struct hostapd_data *hapd, struct sta_info *sta, + const u8 *vht_capab) + { + /* Disable VHT caps for STAs associated to no-VHT BSSes. */ + if (!vht_capab || +- hapd->conf->disable_11ac || ++ !hapd->iconf->ieee80211ac || hapd->conf->disable_11ac || + !check_valid_vht_mcs(hapd->iface->current_mode, vht_capab)) { + sta->flags &= ~WLAN_STA_VHT; + os_free(sta->vht_capabilities); +@@ -159,6 +357,29 @@ + } + + ++u16 copy_sta_vht_oper(struct hostapd_data *hapd, struct sta_info *sta, ++ const u8 *vht_oper) ++{ ++ if (!vht_oper) { ++ os_free(sta->vht_operation); ++ sta->vht_operation = NULL; ++ return WLAN_STATUS_SUCCESS; ++ } ++ ++ if (!sta->vht_operation) { ++ sta->vht_operation = ++ os_zalloc(sizeof(struct ieee80211_vht_operation)); ++ if (!sta->vht_operation) ++ return WLAN_STATUS_UNSPECIFIED_FAILURE; ++ } ++ ++ os_memcpy(sta->vht_operation, vht_oper, ++ sizeof(struct ieee80211_vht_operation)); ++ ++ return WLAN_STATUS_SUCCESS; ++} ++ ++ + u16 copy_sta_vendor_vht(struct hostapd_data *hapd, struct sta_info *sta, + const u8 *ie, size_t len) + { +@@ -212,7 +433,7 @@ + WPA_PUT_BE32(pos, (OUI_BROADCOM << 8) | VENDOR_VHT_TYPE); + pos += 4; + *pos++ = VENDOR_VHT_SUBTYPE; +- pos = hostapd_eid_vht_capabilities(hapd, pos); ++ pos = hostapd_eid_vht_capabilities(hapd, pos, 0); + pos = hostapd_eid_vht_operation(hapd, pos); + + return pos; +--- contrib/wpa/src/ap/ieee802_1x.c.orig ++++ contrib/wpa/src/ap/ieee802_1x.c +@@ -1,6 +1,6 @@ + /* + * hostapd / IEEE 802.1X-2004 Authenticator +- * Copyright (c) 2002-2012, Jouni Malinen ++ * Copyright (c) 2002-2019, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. +@@ -31,9 +31,14 @@ + #include "ap_drv_ops.h" + #include "wps_hostapd.h" + #include "hs20.h" ++/* FIX: Not really a good thing to require ieee802_11.h here.. (FILS) */ ++#include "ieee802_11.h" + #include "ieee802_1x.h" + + ++#ifdef CONFIG_HS20 ++static void ieee802_1x_wnm_notif_send(void *eloop_ctx, void *timeout_ctx); ++#endif /* CONFIG_HS20 */ + static void ieee802_1x_finished(struct hostapd_data *hapd, + struct sta_info *sta, int success, + int remediation); +@@ -219,7 +224,7 @@ + MAC2STR(sta->addr)); + + #ifndef CONFIG_NO_VLAN +- if (sta->vlan_id > 0 && sta->vlan_id <= MAX_VLAN_ID) { ++ if (sta->vlan_id > 0) { + wpa_printf(MSG_ERROR, "Using WEP with vlans is not supported."); + return; + } +@@ -313,6 +318,7 @@ + hdr->code != EAP_CODE_INITIATE)) + return; + ++ eap_erp_update_identity(sm->eap, eap, len); + identity = eap_get_identity(sm->eap, &identity_len); + if (identity == NULL) + return; +@@ -402,7 +408,16 @@ + char buf[128]; + + if (!hostapd_config_get_radius_attr(req_attr, ++ RADIUS_ATTR_SERVICE_TYPE) && ++ !radius_msg_add_attr_int32(msg, RADIUS_ATTR_SERVICE_TYPE, ++ RADIUS_SERVICE_TYPE_FRAMED)) { ++ wpa_printf(MSG_ERROR, "Could not add Service-Type"); ++ return -1; ++ } ++ ++ if (!hostapd_config_get_radius_attr(req_attr, + RADIUS_ATTR_NAS_PORT) && ++ sta->aid > 0 && + !radius_msg_add_attr_int32(msg, RADIUS_ATTR_NAS_PORT, sta->aid)) { + wpa_printf(MSG_ERROR, "Could not add NAS-Port"); + return -1; +@@ -435,9 +450,9 @@ + return -1; + } + +- if (sta->acct_session_id_hi || sta->acct_session_id_lo) { +- os_snprintf(buf, sizeof(buf), "%08X-%08X", +- sta->acct_session_id_hi, sta->acct_session_id_lo); ++ if (sta->acct_session_id) { ++ os_snprintf(buf, sizeof(buf), "%016llX", ++ (unsigned long long) sta->acct_session_id); + if (!radius_msg_add_attr(msg, RADIUS_ATTR_ACCT_SESSION_ID, + (u8 *) buf, os_strlen(buf))) { + wpa_printf(MSG_ERROR, "Could not add Acct-Session-Id"); +@@ -445,7 +460,22 @@ + } + } + +-#ifdef CONFIG_IEEE80211R ++ if ((hapd->conf->wpa & 2) && ++ !hapd->conf->disable_pmksa_caching && ++ sta->eapol_sm && sta->eapol_sm->acct_multi_session_id) { ++ os_snprintf(buf, sizeof(buf), "%016llX", ++ (unsigned long long) ++ sta->eapol_sm->acct_multi_session_id); ++ if (!radius_msg_add_attr( ++ msg, RADIUS_ATTR_ACCT_MULTI_SESSION_ID, ++ (u8 *) buf, os_strlen(buf))) { ++ wpa_printf(MSG_INFO, ++ "Could not add Acct-Multi-Session-Id"); ++ return -1; ++ } ++ } ++ ++#ifdef CONFIG_IEEE80211R_AP + if (hapd->conf->wpa && wpa_key_mgmt_ft(hapd->conf->wpa_key_mgmt) && + sta->wpa_sm && + (wpa_key_mgmt_ft(wpa_auth_sta_key_mgmt(sta->wpa_sm)) || +@@ -458,7 +488,7 @@ + wpa_printf(MSG_ERROR, "Could not add Mobility-Domain-Id"); + return -1; + } +-#endif /* CONFIG_IEEE80211R */ ++#endif /* CONFIG_IEEE80211R_AP */ + + if ((hapd->conf->wpa || hapd->conf->osen) && sta->wpa_sm && + add_common_radius_sta_attr_rsn(hapd, req_attr, sta, msg) < 0) +@@ -475,6 +505,7 @@ + { + char buf[128]; + struct hostapd_radius_attr *attr; ++ int len; + + if (!hostapd_config_get_radius_attr(req_attr, + RADIUS_ATTR_NAS_IP_ADDRESS) && +@@ -506,15 +537,15 @@ + return -1; + } + +- os_snprintf(buf, sizeof(buf), RADIUS_802_1X_ADDR_FORMAT ":%s", +- MAC2STR(hapd->own_addr), +- wpa_ssid_txt(hapd->conf->ssid.ssid, +- hapd->conf->ssid.ssid_len)); +- buf[sizeof(buf) - 1] = '\0'; ++ len = os_snprintf(buf, sizeof(buf), RADIUS_802_1X_ADDR_FORMAT ":", ++ MAC2STR(hapd->own_addr)); ++ os_memcpy(&buf[len], hapd->conf->ssid.ssid, ++ hapd->conf->ssid.ssid_len); ++ len += hapd->conf->ssid.ssid_len; + if (!hostapd_config_get_radius_attr(req_attr, + RADIUS_ATTR_CALLED_STATION_ID) && + !radius_msg_add_attr(msg, RADIUS_ATTR_CALLED_STATION_ID, +- (u8 *) buf, os_strlen(buf))) { ++ (u8 *) buf, len)) { + wpa_printf(MSG_ERROR, "Could not add Called-Station-Id"); + return -1; + } +@@ -560,9 +591,9 @@ + } + + +-static void ieee802_1x_encapsulate_radius(struct hostapd_data *hapd, +- struct sta_info *sta, +- const u8 *eap, size_t len) ++void ieee802_1x_encapsulate_radius(struct hostapd_data *hapd, ++ struct sta_info *sta, ++ const u8 *eap, size_t len) + { + struct radius_msg *msg; + struct eapol_state_machine *sm = sta->eapol_sm; +@@ -583,7 +614,10 @@ + return; + } + +- radius_msg_make_authenticator(msg, (u8 *) sta, sizeof(*sta)); ++ if (radius_msg_make_authenticator(msg) < 0) { ++ wpa_printf(MSG_INFO, "Could not make Request Authenticator"); ++ goto fail; ++ } + + if (sm->identity && + !radius_msg_add_attr(msg, RADIUS_ATTR_USER_NAME, +@@ -648,7 +682,8 @@ + + #ifdef CONFIG_HS20 + if (hapd->conf->hs20) { +- u8 ver = 1; /* Release 2 */ ++ u8 ver = hapd->conf->hs20_release - 1; ++ + if (!radius_msg_add_wfa( + msg, RADIUS_VENDOR_ATTR_WFA_HS20_AP_VERSION, + &ver, 1)) { +@@ -678,6 +713,41 @@ + goto fail; + } + } ++ ++ if (sta->roaming_consortium && ++ !radius_msg_add_wfa( ++ msg, RADIUS_VENDOR_ATTR_WFA_HS20_ROAMING_CONSORTIUM, ++ wpabuf_head(sta->roaming_consortium), ++ wpabuf_len(sta->roaming_consortium))) { ++ wpa_printf(MSG_ERROR, ++ "Could not add HS 2.0 Roaming Consortium"); ++ goto fail; ++ } ++ ++ if (hapd->conf->t_c_filename) { ++ be32 timestamp; ++ ++ if (!radius_msg_add_wfa( ++ msg, ++ RADIUS_VENDOR_ATTR_WFA_HS20_T_C_FILENAME, ++ (const u8 *) hapd->conf->t_c_filename, ++ os_strlen(hapd->conf->t_c_filename))) { ++ wpa_printf(MSG_ERROR, ++ "Could not add HS 2.0 T&C Filename"); ++ goto fail; ++ } ++ ++ timestamp = host_to_be32(hapd->conf->t_c_timestamp); ++ if (!radius_msg_add_wfa( ++ msg, ++ RADIUS_VENDOR_ATTR_WFA_HS20_TIMESTAMP, ++ (const u8 *) ×tamp, ++ sizeof(timestamp))) { ++ wpa_printf(MSG_ERROR, ++ "Could not add HS 2.0 Timestamp"); ++ goto fail; ++ } ++ } + } + #endif /* CONFIG_HS20 */ + +@@ -814,7 +884,7 @@ + } + + +-static struct eapol_state_machine * ++struct eapol_state_machine * + ieee802_1x_alloc_eapol_sm(struct hostapd_data *hapd, struct sta_info *sta) + { + int flags = 0; +@@ -831,6 +901,29 @@ + } + + ++static void ieee802_1x_save_eapol(struct sta_info *sta, const u8 *buf, ++ size_t len) ++{ ++ if (sta->pending_eapol_rx) { ++ wpabuf_free(sta->pending_eapol_rx->buf); ++ } else { ++ sta->pending_eapol_rx = ++ os_malloc(sizeof(*sta->pending_eapol_rx)); ++ if (!sta->pending_eapol_rx) ++ return; ++ } ++ ++ sta->pending_eapol_rx->buf = wpabuf_alloc_copy(buf, len); ++ if (!sta->pending_eapol_rx->buf) { ++ os_free(sta->pending_eapol_rx); ++ sta->pending_eapol_rx = NULL; ++ return; ++ } ++ ++ os_get_reltime(&sta->pending_eapol_rx->rx_time); ++} ++ ++ + /** + * ieee802_1x_receive - Process the EAPOL frames from the Supplicant + * @hapd: hostapd BSS data +@@ -861,6 +954,13 @@ + !(hapd->iface->drv_flags & WPA_DRIVER_FLAGS_WIRED))) { + wpa_printf(MSG_DEBUG, "IEEE 802.1X data frame from not " + "associated/Pre-authenticating STA"); ++ ++ if (sta && (sta->flags & WLAN_STA_AUTH)) { ++ wpa_printf(MSG_DEBUG, "Saving EAPOL frame from " MACSTR ++ " for later use", MAC2STR(sta->addr)); ++ ieee802_1x_save_eapol(sta, buf, len); ++ } ++ + return; + } + +@@ -909,7 +1009,9 @@ + } + + key_mgmt = wpa_auth_sta_key_mgmt(sta->wpa_sm); +- if (key_mgmt != -1 && wpa_key_mgmt_wpa_psk(key_mgmt)) { ++ if (key_mgmt != -1 && ++ (wpa_key_mgmt_wpa_psk(key_mgmt) || key_mgmt == WPA_KEY_MGMT_OWE || ++ key_mgmt == WPA_KEY_MGMT_DPP)) { + wpa_printf(MSG_DEBUG, "IEEE 802.1X: Ignore EAPOL message - " + "STA is using PSK"); + return; +@@ -1047,18 +1149,20 @@ + * Clear any possible EAPOL authenticator state to support + * reassociation change from WPS to PSK. + */ +- ieee802_1x_free_station(sta); ++ ieee802_1x_free_station(hapd, sta); + return; + } + + key_mgmt = wpa_auth_sta_key_mgmt(sta->wpa_sm); +- if (key_mgmt != -1 && wpa_key_mgmt_wpa_psk(key_mgmt)) { ++ if (key_mgmt != -1 && ++ (wpa_key_mgmt_wpa_psk(key_mgmt) || key_mgmt == WPA_KEY_MGMT_OWE || ++ key_mgmt == WPA_KEY_MGMT_DPP)) { + wpa_printf(MSG_DEBUG, "IEEE 802.1X: Ignore STA - using PSK"); + /* + * Clear any possible EAPOL authenticator state to support + * reassociation change from WPA-EAP to PSK. + */ +- ieee802_1x_free_station(sta); ++ ieee802_1x_free_station(hapd, sta); + return; + } + +@@ -1093,7 +1197,7 @@ + + sta->eapol_sm->eap_if->portEnabled = TRUE; + +-#ifdef CONFIG_IEEE80211R ++#ifdef CONFIG_IEEE80211R_AP + if (sta->auth_alg == WLAN_AUTH_FT) { + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X, + HOSTAPD_LEVEL_DEBUG, +@@ -1106,13 +1210,37 @@ + sta->eapol_sm->be_auth_state = BE_AUTH_SUCCESS; + sta->eapol_sm->authSuccess = TRUE; + sta->eapol_sm->authFail = FALSE; ++ sta->eapol_sm->portValid = TRUE; + if (sta->eapol_sm->eap) + eap_sm_notify_cached(sta->eapol_sm->eap); +- /* TODO: get vlan_id from R0KH using RRB message */ ++ ap_sta_bind_vlan(hapd, sta); + return; + } +-#endif /* CONFIG_IEEE80211R */ ++#endif /* CONFIG_IEEE80211R_AP */ + ++#ifdef CONFIG_FILS ++ if (sta->auth_alg == WLAN_AUTH_FILS_SK || ++ sta->auth_alg == WLAN_AUTH_FILS_SK_PFS || ++ sta->auth_alg == WLAN_AUTH_FILS_PK) { ++ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X, ++ HOSTAPD_LEVEL_DEBUG, ++ "PMK from FILS - skip IEEE 802.1X/EAP"); ++ /* Setup EAPOL state machines to already authenticated state ++ * because of existing FILS information. */ ++ sta->eapol_sm->keyRun = TRUE; ++ sta->eapol_sm->eap_if->eapKeyAvailable = TRUE; ++ sta->eapol_sm->auth_pae_state = AUTH_PAE_AUTHENTICATING; ++ sta->eapol_sm->be_auth_state = BE_AUTH_SUCCESS; ++ sta->eapol_sm->authSuccess = TRUE; ++ sta->eapol_sm->authFail = FALSE; ++ sta->eapol_sm->portValid = TRUE; ++ if (sta->eapol_sm->eap) ++ eap_sm_notify_cached(sta->eapol_sm->eap); ++ wpa_auth_set_ptk_rekey_timer(sta->wpa_sm); ++ return; ++ } ++#endif /* CONFIG_FILS */ ++ + pmksa = wpa_auth_sta_get_pmksa(sta->wpa_sm); + if (pmksa) { + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X, +@@ -1128,7 +1256,7 @@ + sta->eapol_sm->authFail = FALSE; + if (sta->eapol_sm->eap) + eap_sm_notify_cached(sta->eapol_sm->eap); +- pmksa_cache_to_eapol_data(pmksa, sta->eapol_sm); ++ pmksa_cache_to_eapol_data(hapd, pmksa, sta->eapol_sm); + ap_sta_bind_vlan(hapd, sta); + } else { + if (reassoc) { +@@ -1144,10 +1272,20 @@ + } + + +-void ieee802_1x_free_station(struct sta_info *sta) ++void ieee802_1x_free_station(struct hostapd_data *hapd, struct sta_info *sta) + { + struct eapol_state_machine *sm = sta->eapol_sm; + ++#ifdef CONFIG_HS20 ++ eloop_cancel_timeout(ieee802_1x_wnm_notif_send, hapd, sta); ++#endif /* CONFIG_HS20 */ ++ ++ if (sta->pending_eapol_rx) { ++ wpabuf_free(sta->pending_eapol_rx->buf); ++ os_free(sta->pending_eapol_rx); ++ sta->pending_eapol_rx = NULL; ++ } ++ + if (sm == NULL) + return; + +@@ -1156,10 +1294,8 @@ + #ifndef CONFIG_NO_RADIUS + radius_msg_free(sm->last_recv_radius); + radius_free_class(&sm->radius_class); +- wpabuf_free(sm->radius_cui); + #endif /* CONFIG_NO_RADIUS */ + +- os_free(sm->identity); + eapol_auth_free(sm); + } + +@@ -1325,11 +1461,10 @@ + } + } while (class_len < 1); + +- nclass[nclass_count].data = os_malloc(class_len); ++ nclass[nclass_count].data = os_memdup(attr_class, class_len); + if (nclass[nclass_count].data == NULL) + break; + +- os_memcpy(nclass[nclass_count].data, attr_class, class_len); + nclass[nclass_count].len = class_len; + nclass_count++; + } +@@ -1489,6 +1624,33 @@ + ap_sta_session_warning_timeout(hapd, sta, warning_time); + } + ++ ++static void ieee802_1x_hs20_t_c_filtering(struct hostapd_data *hapd, ++ struct sta_info *sta, u8 *pos, ++ size_t len) ++{ ++ if (len < 4) ++ return; /* Malformed information */ ++ wpa_printf(MSG_DEBUG, ++ "HS 2.0: Terms and Conditions filtering %02x %02x %02x %02x", ++ pos[0], pos[1], pos[2], pos[3]); ++ hs20_t_c_filtering(hapd, sta, pos[0] & BIT(0)); ++} ++ ++ ++static void ieee802_1x_hs20_t_c_url(struct hostapd_data *hapd, ++ struct sta_info *sta, u8 *pos, size_t len) ++{ ++ os_free(sta->t_c_url); ++ sta->t_c_url = os_malloc(len + 1); ++ if (!sta->t_c_url) ++ return; ++ os_memcpy(sta->t_c_url, pos, len); ++ sta->t_c_url[len] = '\0'; ++ wpa_printf(MSG_DEBUG, ++ "HS 2.0: Terms and Conditions URL %s", sta->t_c_url); ++} ++ + #endif /* CONFIG_HS20 */ + + +@@ -1536,6 +1698,12 @@ + ieee802_1x_hs20_session_info(hapd, sta, pos, sublen, + session_timeout); + break; ++ case RADIUS_VENDOR_ATTR_WFA_HS20_T_C_FILTERING: ++ ieee802_1x_hs20_t_c_filtering(hapd, sta, pos, sublen); ++ break; ++ case RADIUS_VENDOR_ATTR_WFA_HS20_T_C_URL: ++ ieee802_1x_hs20_t_c_url(hapd, sta, pos, sublen); ++ break; + } + } + #endif /* CONFIG_HS20 */ +@@ -1575,6 +1743,45 @@ + } + + ++#ifndef CONFIG_NO_VLAN ++static int ieee802_1x_update_vlan(struct radius_msg *msg, ++ struct hostapd_data *hapd, ++ struct sta_info *sta) ++{ ++ struct vlan_description vlan_desc; ++ ++ os_memset(&vlan_desc, 0, sizeof(vlan_desc)); ++ vlan_desc.notempty = !!radius_msg_get_vlanid(msg, &vlan_desc.untagged, ++ MAX_NUM_TAGGED_VLAN, ++ vlan_desc.tagged); ++ ++ if (vlan_desc.notempty && ++ !hostapd_vlan_valid(hapd->conf->vlan, &vlan_desc)) { ++ sta->eapol_sm->authFail = TRUE; ++ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_RADIUS, ++ HOSTAPD_LEVEL_INFO, ++ "Invalid VLAN %d%s received from RADIUS server", ++ vlan_desc.untagged, ++ vlan_desc.tagged[0] ? "+" : ""); ++ os_memset(&vlan_desc, 0, sizeof(vlan_desc)); ++ ap_sta_set_vlan(hapd, sta, &vlan_desc); ++ return -1; ++ } ++ ++ if (hapd->conf->ssid.dynamic_vlan == DYNAMIC_VLAN_REQUIRED && ++ !vlan_desc.notempty) { ++ sta->eapol_sm->authFail = TRUE; ++ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X, ++ HOSTAPD_LEVEL_INFO, ++ "authentication server did not include required VLAN ID in Access-Accept"); ++ return -1; ++ } ++ ++ return ap_sta_set_vlan(hapd, sta, &vlan_desc); ++} ++#endif /* CONFIG_NO_VLAN */ ++ ++ + /** + * ieee802_1x_receive_auth - Process RADIUS frames from Authentication Server + * @msg: RADIUS response message +@@ -1592,7 +1799,8 @@ + struct hostapd_data *hapd = data; + struct sta_info *sta; + u32 session_timeout = 0, termination_action, acct_interim_interval; +- int session_timeout_set, vlan_id = 0; ++ int session_timeout_set; ++ u32 reason_code; + struct eapol_state_machine *sm; + int override_eapReq = 0; + struct radius_hdr *hdr = radius_msg_get_hdr(msg); +@@ -1659,51 +1867,35 @@ + + switch (hdr->code) { + case RADIUS_CODE_ACCESS_ACCEPT: +- if (hapd->conf->ssid.dynamic_vlan == DYNAMIC_VLAN_DISABLED) +- vlan_id = 0; + #ifndef CONFIG_NO_VLAN +- else +- vlan_id = radius_msg_get_vlanid(msg); +- if (vlan_id > 0 && +- hostapd_vlan_id_valid(hapd->conf->vlan, vlan_id)) { ++ if (hapd->conf->ssid.dynamic_vlan != DYNAMIC_VLAN_DISABLED && ++ ieee802_1x_update_vlan(msg, hapd, sta) < 0) ++ break; ++ ++ if (sta->vlan_id > 0) { + hostapd_logger(hapd, sta->addr, + HOSTAPD_MODULE_RADIUS, + HOSTAPD_LEVEL_INFO, +- "VLAN ID %d", vlan_id); +- } else if (vlan_id > 0) { +- sta->eapol_sm->authFail = TRUE; +- hostapd_logger(hapd, sta->addr, +- HOSTAPD_MODULE_RADIUS, +- HOSTAPD_LEVEL_INFO, +- "Invalid VLAN ID %d received from RADIUS server", +- vlan_id); +- break; +- } else if (hapd->conf->ssid.dynamic_vlan == +- DYNAMIC_VLAN_REQUIRED) { +- sta->eapol_sm->authFail = TRUE; +- hostapd_logger(hapd, sta->addr, +- HOSTAPD_MODULE_IEEE8021X, +- HOSTAPD_LEVEL_INFO, "authentication " +- "server did not include required VLAN " +- "ID in Access-Accept"); +- break; ++ "VLAN ID %d", sta->vlan_id); + } +-#endif /* CONFIG_NO_VLAN */ + +- sta->vlan_id = vlan_id; + if ((sta->flags & WLAN_STA_ASSOC) && + ap_sta_bind_vlan(hapd, sta) < 0) + break; ++#endif /* CONFIG_NO_VLAN */ + + sta->session_timeout_set = !!session_timeout_set; +- sta->session_timeout = session_timeout; ++ os_get_reltime(&sta->session_timeout); ++ sta->session_timeout.sec += session_timeout; + + /* RFC 3580, Ch. 3.17 */ + if (session_timeout_set && termination_action == +- RADIUS_TERMINATION_ACTION_RADIUS_REQUEST) { ++ RADIUS_TERMINATION_ACTION_RADIUS_REQUEST) + sm->reAuthPeriod = session_timeout; +- } else if (session_timeout_set) ++ else if (session_timeout_set) + ap_sta_session_timeout(hapd, sta, session_timeout); ++ else ++ ap_sta_no_session_timeout(hapd, sta); + + sm->eap_if->aaaSuccess = TRUE; + override_eapReq = 1; +@@ -1715,19 +1907,17 @@ + ieee802_1x_check_hs20(hapd, sta, msg, + session_timeout_set ? + (int) session_timeout : -1); +- if (sm->eap_if->eapKeyAvailable && !sta->remediation && +- !sta->hs20_deauth_requested && +- wpa_auth_pmksa_add(sta->wpa_sm, sm->eapol_key_crypt, +- session_timeout_set ? +- (int) session_timeout : -1, sm) == 0) { +- hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA, +- HOSTAPD_LEVEL_DEBUG, +- "Added PMKSA cache entry"); +- } + break; + case RADIUS_CODE_ACCESS_REJECT: + sm->eap_if->aaaFail = TRUE; + override_eapReq = 1; ++ if (radius_msg_get_attr_int32(msg, RADIUS_ATTR_WLAN_REASON_CODE, ++ &reason_code) == 0) { ++ wpa_printf(MSG_DEBUG, ++ "RADIUS server indicated WLAN-Reason-Code %u in Access-Reject for " ++ MACSTR, reason_code, MAC2STR(sta->addr)); ++ sta->disconnect_reason_code = reason_code; ++ } + break; + case RADIUS_CODE_ACCESS_CHALLENGE: + sm->eap_if->aaaEapReq = TRUE; +@@ -1754,6 +1944,19 @@ + if (override_eapReq) + sm->eap_if->aaaEapReq = FALSE; + ++#ifdef CONFIG_FILS ++#ifdef NEED_AP_MLME ++ if (sta->flags & WLAN_STA_PENDING_FILS_ERP) { ++ /* TODO: Add a PMKSA entry on success? */ ++ ieee802_11_finish_fils_auth( ++ hapd, sta, hdr->code == RADIUS_CODE_ACCESS_ACCEPT, ++ sm->eap_if->aaaEapReqData, ++ sm->eap_if->aaaEapKeyData, ++ sm->eap_if->aaaEapKeyDataLen); ++ } ++#endif /* NEED_AP_MLME */ ++#endif /* CONFIG_FILS */ ++ + eapol_auth_step(sm); + + return RADIUS_RX_QUEUED; +@@ -1841,7 +2044,7 @@ + + wpa_printf(MSG_DEBUG, "IEEE 802.1X: New default WEP key index %d", + eapol->default_wep_key_idx); +- ++ + if (ieee802_1x_rekey_broadcast(hapd)) { + hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE8021X, + HOSTAPD_LEVEL_WARNING, "failed to generate a " +@@ -1951,13 +2154,19 @@ + } + + if (eap_user->password) { +- user->password = os_malloc(eap_user->password_len); ++ user->password = os_memdup(eap_user->password, ++ eap_user->password_len); + if (user->password == NULL) + goto out; +- os_memcpy(user->password, eap_user->password, +- eap_user->password_len); + user->password_len = eap_user->password_len; + user->password_hash = eap_user->password_hash; ++ if (eap_user->salt && eap_user->salt_len) { ++ user->salt = os_memdup(eap_user->salt, ++ eap_user->salt_len); ++ if (!user->salt) ++ goto out; ++ user->salt_len = eap_user->salt_len; ++ } + } + user->force_version = eap_user->force_version; + user->macacl = eap_user->macacl; +@@ -2107,6 +2316,7 @@ + conf.erp_domain = hapd->conf->erp_domain; + conf.erp = hapd->conf->eap_server_erp; + conf.tls_session_lifetime = hapd->conf->tls_session_lifetime; ++ conf.tls_flags = hapd->conf->tls_flags; + conf.pac_opaque_encr_key = hapd->conf->pac_opaque_encr_key; + conf.eap_fast_a_id = hapd->conf->eap_fast_a_id; + conf.eap_fast_a_id_len = hapd->conf->eap_fast_a_id_len; +@@ -2190,7 +2400,7 @@ + { + eloop_cancel_timeout(ieee802_1x_rekey, hapd, NULL); + +- if (hapd->driver != NULL && ++ if (hapd->driver && hapd->drv_priv && + (hapd->conf->ieee802_1x || hapd->conf->wpa)) + hostapd_set_drv_ieee8021x(hapd, hapd->conf->iface, 0); + +@@ -2243,6 +2453,16 @@ + MAC2STR(sta->addr), xhdr->version, xhdr->type, + be_to_host16(xhdr->length), ack); + ++#ifdef CONFIG_WPS ++ if (xhdr->type == IEEE802_1X_TYPE_EAP_PACKET && ack && ++ (sta->flags & WLAN_STA_WPS) && ++ ap_sta_pending_delayed_1x_auth_fail_disconnect(hapd, sta)) { ++ wpa_printf(MSG_DEBUG, ++ "WPS: Indicate EAP completion on ACK for EAP-Failure"); ++ hostapd_wps_eap_completed(hapd); ++ } ++#endif /* CONFIG_WPS */ ++ + if (xhdr->type != IEEE802_1X_TYPE_EAPOL_KEY) + return 0; + +@@ -2375,6 +2595,7 @@ + struct os_reltime diff; + const char *name1; + const char *name2; ++ char *identity_buf = NULL; + + if (sm == NULL) + return 0; +@@ -2490,31 +2711,41 @@ + + /* dot1xAuthSessionStatsTable */ + os_reltime_age(&sta->acct_session_start, &diff); ++ if (sm->eap && !sm->identity) { ++ const u8 *id; ++ size_t id_len; ++ ++ id = eap_get_identity(sm->eap, &id_len); ++ if (id) ++ identity_buf = dup_binstr(id, id_len); ++ } + ret = os_snprintf(buf + len, buflen - len, + /* TODO: dot1xAuthSessionOctetsRx */ + /* TODO: dot1xAuthSessionOctetsTx */ + /* TODO: dot1xAuthSessionFramesRx */ + /* TODO: dot1xAuthSessionFramesTx */ +- "dot1xAuthSessionId=%08X-%08X\n" ++ "dot1xAuthSessionId=%016llX\n" + "dot1xAuthSessionAuthenticMethod=%d\n" + "dot1xAuthSessionTime=%u\n" + "dot1xAuthSessionTerminateCause=999\n" + "dot1xAuthSessionUserName=%s\n", +- sta->acct_session_id_hi, sta->acct_session_id_lo, ++ (unsigned long long) sta->acct_session_id, + (wpa_key_mgmt_wpa_ieee8021x( + wpa_auth_sta_key_mgmt(sta->wpa_sm))) ? + 1 : 2, + (unsigned int) diff.sec, +- sm->identity); ++ sm->identity ? (char *) sm->identity : ++ (identity_buf ? identity_buf : "N/A")); ++ os_free(identity_buf); + if (os_snprintf_error(buflen - len, ret)) + return len; + len += ret; + +- if (sm->acct_multi_session_id_hi) { ++ if (sm->acct_multi_session_id) { + ret = os_snprintf(buf + len, buflen - len, +- "authMultiSessionId=%08X+%08X\n", +- sm->acct_multi_session_id_hi, +- sm->acct_multi_session_id_lo); ++ "authMultiSessionId=%016llX\n", ++ (unsigned long long) ++ sm->acct_multi_session_id); + if (os_snprintf_error(buflen - len, ret)) + return len; + len += ret; +@@ -2535,6 +2766,43 @@ + } + + ++#ifdef CONFIG_HS20 ++static void ieee802_1x_wnm_notif_send(void *eloop_ctx, void *timeout_ctx) ++{ ++ struct hostapd_data *hapd = eloop_ctx; ++ struct sta_info *sta = timeout_ctx; ++ ++ if (sta->remediation) { ++ wpa_printf(MSG_DEBUG, "HS 2.0: Send WNM-Notification to " ++ MACSTR " to indicate Subscription Remediation", ++ MAC2STR(sta->addr)); ++ hs20_send_wnm_notification(hapd, sta->addr, ++ sta->remediation_method, ++ sta->remediation_url); ++ os_free(sta->remediation_url); ++ sta->remediation_url = NULL; ++ } ++ ++ if (sta->hs20_deauth_req) { ++ wpa_printf(MSG_DEBUG, "HS 2.0: Send WNM-Notification to " ++ MACSTR " to indicate imminent deauthentication", ++ MAC2STR(sta->addr)); ++ hs20_send_wnm_notification_deauth_req(hapd, sta->addr, ++ sta->hs20_deauth_req); ++ } ++ ++ if (sta->hs20_t_c_filtering) { ++ wpa_printf(MSG_DEBUG, "HS 2.0: Send WNM-Notification to " ++ MACSTR " to indicate Terms and Conditions filtering", ++ MAC2STR(sta->addr)); ++ hs20_send_wnm_notification_t_c(hapd, sta->addr, sta->t_c_url); ++ os_free(sta->t_c_url); ++ sta->t_c_url = NULL; ++ } ++} ++#endif /* CONFIG_HS20 */ ++ ++ + static void ieee802_1x_finished(struct hostapd_data *hapd, + struct sta_info *sta, int success, + int remediation) +@@ -2544,6 +2812,7 @@ + /* TODO: get PMKLifetime from WPA parameters */ + static const int dot11RSNAConfigPMKLifetime = 43200; + unsigned int session_timeout; ++ struct os_reltime now, remaining; + + #ifdef CONFIG_HS20 + if (remediation && !sta->remediation) { +@@ -2554,37 +2823,27 @@ + sta->remediation_method = 1; /* SOAP-XML SPP */ + } + +- if (success) { +- if (sta->remediation) { +- wpa_printf(MSG_DEBUG, "HS 2.0: Send WNM-Notification " +- "to " MACSTR " to indicate Subscription " +- "Remediation", +- MAC2STR(sta->addr)); +- hs20_send_wnm_notification(hapd, sta->addr, +- sta->remediation_method, +- sta->remediation_url); +- os_free(sta->remediation_url); +- sta->remediation_url = NULL; +- } +- +- if (sta->hs20_deauth_req) { +- wpa_printf(MSG_DEBUG, "HS 2.0: Send WNM-Notification " +- "to " MACSTR " to indicate imminent " +- "deauthentication", MAC2STR(sta->addr)); +- hs20_send_wnm_notification_deauth_req( +- hapd, sta->addr, sta->hs20_deauth_req); +- } ++ if (success && (sta->remediation || sta->hs20_deauth_req || ++ sta->hs20_t_c_filtering)) { ++ wpa_printf(MSG_DEBUG, "HS 2.0: Schedule WNM-Notification to " ++ MACSTR " in 100 ms", MAC2STR(sta->addr)); ++ eloop_cancel_timeout(ieee802_1x_wnm_notif_send, hapd, sta); ++ eloop_register_timeout(0, 100000, ieee802_1x_wnm_notif_send, ++ hapd, sta); + } + #endif /* CONFIG_HS20 */ + + key = ieee802_1x_get_key(sta->eapol_sm, &len); +- if (sta->session_timeout_set) +- session_timeout = sta->session_timeout; +- else ++ if (sta->session_timeout_set) { ++ os_get_reltime(&now); ++ os_reltime_sub(&sta->session_timeout, &now, &remaining); ++ session_timeout = (remaining.sec > 0) ? remaining.sec : 1; ++ } else { + session_timeout = dot11RSNAConfigPMKLifetime; ++ } + if (success && key && len >= PMK_LEN && !sta->remediation && + !sta->hs20_deauth_requested && +- wpa_auth_pmksa_add(sta->wpa_sm, key, session_timeout, ++ wpa_auth_pmksa_add(sta->wpa_sm, key, len, session_timeout, + sta->eapol_sm) == 0) { + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA, + HOSTAPD_LEVEL_DEBUG, +@@ -2602,15 +2861,6 @@ + * EAP-FAST with anonymous provisioning, may require another + * EAPOL authentication to be started to complete connection. + */ +- wpa_dbg(hapd->msg_ctx, MSG_DEBUG, "IEEE 802.1X: Force " +- "disconnection after EAP-Failure"); +- /* Add a small sleep to increase likelihood of previously +- * requested EAP-Failure TX getting out before this should the +- * driver reorder operations. +- */ +- os_sleep(0, 10000); +- ap_sta_disconnect(hapd, sta, sta->addr, +- WLAN_REASON_IEEE_802_1X_AUTH_FAILED); +- hostapd_wps_eap_completed(hapd); ++ ap_sta_delayed_1x_auth_fail_disconnect(hapd, sta); + } + } +--- contrib/wpa/src/ap/ieee802_1x.h.orig ++++ contrib/wpa/src/ap/ieee802_1x.h +@@ -21,7 +21,7 @@ + void ieee802_1x_receive(struct hostapd_data *hapd, const u8 *sa, const u8 *buf, + size_t len); + void ieee802_1x_new_station(struct hostapd_data *hapd, struct sta_info *sta); +-void ieee802_1x_free_station(struct sta_info *sta); ++void ieee802_1x_free_station(struct hostapd_data *hapd, struct sta_info *sta); + + void ieee802_1x_abort_auth(struct hostapd_data *hapd, struct sta_info *sta); + void ieee802_1x_set_sta_authorized(struct hostapd_data *hapd, +@@ -57,5 +57,10 @@ + struct hostapd_radius_attr *req_attr, + struct sta_info *sta, + struct radius_msg *msg); ++void ieee802_1x_encapsulate_radius(struct hostapd_data *hapd, ++ struct sta_info *sta, ++ const u8 *eap, size_t len); ++struct eapol_state_machine * ++ieee802_1x_alloc_eapol_sm(struct hostapd_data *hapd, struct sta_info *sta); + + #endif /* IEEE802_1X_H */ +--- contrib/wpa/src/ap/mbo_ap.c.orig ++++ contrib/wpa/src/ap/mbo_ap.c +@@ -0,0 +1,244 @@ ++/* ++ * hostapd - MBO ++ * Copyright (c) 2016, Qualcomm Atheros, Inc. ++ * ++ * This software may be distributed under the terms of the BSD license. ++ * See README for more details. ++ */ ++ ++#include "utils/includes.h" ++ ++#include "utils/common.h" ++#include "common/ieee802_11_defs.h" ++#include "common/ieee802_11_common.h" ++#include "hostapd.h" ++#include "sta_info.h" ++#include "mbo_ap.h" ++ ++ ++void mbo_ap_sta_free(struct sta_info *sta) ++{ ++ struct mbo_non_pref_chan_info *info, *prev; ++ ++ info = sta->non_pref_chan; ++ sta->non_pref_chan = NULL; ++ while (info) { ++ prev = info; ++ info = info->next; ++ os_free(prev); ++ } ++} ++ ++ ++static void mbo_ap_parse_non_pref_chan(struct sta_info *sta, ++ const u8 *buf, size_t len) ++{ ++ struct mbo_non_pref_chan_info *info, *tmp; ++ char channels[200], *pos, *end; ++ size_t num_chan, i; ++ int ret; ++ ++ if (len <= 3) ++ return; /* Not enough room for any channels */ ++ ++ num_chan = len - 3; ++ info = os_zalloc(sizeof(*info) + num_chan); ++ if (!info) ++ return; ++ info->op_class = buf[0]; ++ info->pref = buf[len - 2]; ++ info->reason_code = buf[len - 1]; ++ info->num_channels = num_chan; ++ buf++; ++ os_memcpy(info->channels, buf, num_chan); ++ if (!sta->non_pref_chan) { ++ sta->non_pref_chan = info; ++ } else { ++ tmp = sta->non_pref_chan; ++ while (tmp->next) ++ tmp = tmp->next; ++ tmp->next = info; ++ } ++ ++ pos = channels; ++ end = pos + sizeof(channels); ++ *pos = '\0'; ++ for (i = 0; i < num_chan; i++) { ++ ret = os_snprintf(pos, end - pos, "%s%u", ++ i == 0 ? "" : " ", buf[i]); ++ if (os_snprintf_error(end - pos, ret)) { ++ *pos = '\0'; ++ break; ++ } ++ pos += ret; ++ } ++ ++ wpa_printf(MSG_DEBUG, "MBO: STA " MACSTR ++ " non-preferred channel list (op class %u, pref %u, reason code %u, channels %s)", ++ MAC2STR(sta->addr), info->op_class, info->pref, ++ info->reason_code, channels); ++} ++ ++ ++void mbo_ap_check_sta_assoc(struct hostapd_data *hapd, struct sta_info *sta, ++ struct ieee802_11_elems *elems) ++{ ++ const u8 *pos, *attr, *end; ++ size_t len; ++ ++ if (!hapd->conf->mbo_enabled || !elems->mbo) ++ return; ++ ++ pos = elems->mbo + 4; ++ len = elems->mbo_len - 4; ++ wpa_hexdump(MSG_DEBUG, "MBO: Association Request attributes", pos, len); ++ ++ attr = get_ie(pos, len, MBO_ATTR_ID_CELL_DATA_CAPA); ++ if (attr && attr[1] >= 1) ++ sta->cell_capa = attr[2]; ++ ++ mbo_ap_sta_free(sta); ++ end = pos + len; ++ while (end - pos > 1) { ++ u8 ie_len = pos[1]; ++ ++ if (2 + ie_len > end - pos) ++ break; ++ ++ if (pos[0] == MBO_ATTR_ID_NON_PREF_CHAN_REPORT) ++ mbo_ap_parse_non_pref_chan(sta, pos + 2, ie_len); ++ pos += 2 + pos[1]; ++ } ++} ++ ++ ++int mbo_ap_get_info(struct sta_info *sta, char *buf, size_t buflen) ++{ ++ char *pos = buf, *end = buf + buflen; ++ int ret; ++ struct mbo_non_pref_chan_info *info; ++ u8 i; ++ unsigned int count = 0; ++ ++ if (!sta->cell_capa) ++ return 0; ++ ++ ret = os_snprintf(pos, end - pos, "mbo_cell_capa=%u\n", sta->cell_capa); ++ if (os_snprintf_error(end - pos, ret)) ++ return pos - buf; ++ pos += ret; ++ ++ for (info = sta->non_pref_chan; info; info = info->next) { ++ char *pos2 = pos; ++ ++ ret = os_snprintf(pos2, end - pos2, ++ "non_pref_chan[%u]=%u:%u:%u:", ++ count, info->op_class, info->pref, ++ info->reason_code); ++ count++; ++ if (os_snprintf_error(end - pos2, ret)) ++ break; ++ pos2 += ret; ++ ++ for (i = 0; i < info->num_channels; i++) { ++ ret = os_snprintf(pos2, end - pos2, "%u%s", ++ info->channels[i], ++ i + 1 < info->num_channels ? ++ "," : ""); ++ if (os_snprintf_error(end - pos2, ret)) { ++ pos2 = NULL; ++ break; ++ } ++ pos2 += ret; ++ } ++ ++ if (!pos2) ++ break; ++ ret = os_snprintf(pos2, end - pos2, "\n"); ++ if (os_snprintf_error(end - pos2, ret)) ++ break; ++ pos2 += ret; ++ pos = pos2; ++ } ++ ++ return pos - buf; ++} ++ ++ ++static void mbo_ap_wnm_notif_req_cell_capa(struct sta_info *sta, ++ const u8 *buf, size_t len) ++{ ++ if (len < 1) ++ return; ++ wpa_printf(MSG_DEBUG, "MBO: STA " MACSTR ++ " updated cellular data capability: %u", ++ MAC2STR(sta->addr), buf[0]); ++ sta->cell_capa = buf[0]; ++} ++ ++ ++static void mbo_ap_wnm_notif_req_elem(struct sta_info *sta, u8 type, ++ const u8 *buf, size_t len, ++ int *first_non_pref_chan) ++{ ++ switch (type) { ++ case WFA_WNM_NOTIF_SUBELEM_NON_PREF_CHAN_REPORT: ++ if (*first_non_pref_chan) { ++ /* ++ * Need to free the previously stored entries now to ++ * allow the update to replace all entries. ++ */ ++ *first_non_pref_chan = 0; ++ mbo_ap_sta_free(sta); ++ } ++ mbo_ap_parse_non_pref_chan(sta, buf, len); ++ break; ++ case WFA_WNM_NOTIF_SUBELEM_CELL_DATA_CAPA: ++ mbo_ap_wnm_notif_req_cell_capa(sta, buf, len); ++ break; ++ default: ++ wpa_printf(MSG_DEBUG, ++ "MBO: Ignore unknown WNM Notification WFA subelement %u", ++ type); ++ break; ++ } ++} ++ ++ ++void mbo_ap_wnm_notification_req(struct hostapd_data *hapd, const u8 *addr, ++ const u8 *buf, size_t len) ++{ ++ const u8 *pos, *end; ++ u8 ie_len; ++ struct sta_info *sta; ++ int first_non_pref_chan = 1; ++ ++ if (!hapd->conf->mbo_enabled) ++ return; ++ ++ sta = ap_get_sta(hapd, addr); ++ if (!sta) ++ return; ++ ++ pos = buf; ++ end = buf + len; ++ ++ while (end - pos > 1) { ++ ie_len = pos[1]; ++ ++ if (2 + ie_len > end - pos) ++ break; ++ ++ if (pos[0] == WLAN_EID_VENDOR_SPECIFIC && ++ ie_len >= 4 && WPA_GET_BE24(pos + 2) == OUI_WFA) ++ mbo_ap_wnm_notif_req_elem(sta, pos[5], ++ pos + 6, ie_len - 4, ++ &first_non_pref_chan); ++ else ++ wpa_printf(MSG_DEBUG, ++ "MBO: Ignore unknown WNM Notification element %u (len=%u)", ++ pos[0], pos[1]); ++ ++ pos += 2 + pos[1]; ++ } ++} +--- contrib/wpa/src/ap/mbo_ap.h.orig ++++ contrib/wpa/src/ap/mbo_ap.h +@@ -0,0 +1,51 @@ ++/* ++ * MBO related functions and structures ++ * Copyright (c) 2016, Qualcomm Atheros, Inc. ++ * ++ * This software may be distributed under the terms of the BSD license. ++ * See README for more details. ++ */ ++ ++#ifndef MBO_AP_H ++#define MBO_AP_H ++ ++struct hostapd_data; ++struct sta_info; ++struct ieee802_11_elems; ++ ++#ifdef CONFIG_MBO ++ ++void mbo_ap_check_sta_assoc(struct hostapd_data *hapd, struct sta_info *sta, ++ struct ieee802_11_elems *elems); ++int mbo_ap_get_info(struct sta_info *sta, char *buf, size_t buflen); ++void mbo_ap_wnm_notification_req(struct hostapd_data *hapd, const u8 *addr, ++ const u8 *buf, size_t len); ++void mbo_ap_sta_free(struct sta_info *sta); ++ ++#else /* CONFIG_MBO */ ++ ++static inline void mbo_ap_check_sta_assoc(struct hostapd_data *hapd, ++ struct sta_info *sta, ++ struct ieee802_11_elems *elems) ++{ ++} ++ ++static inline int mbo_ap_get_info(struct sta_info *sta, char *buf, ++ size_t buflen) ++{ ++ return 0; ++} ++ ++static inline void mbo_ap_wnm_notification_req(struct hostapd_data *hapd, ++ const u8 *addr, ++ const u8 *buf, size_t len) ++{ ++} ++ ++static inline void mbo_ap_sta_free(struct sta_info *sta) ++{ ++} ++ ++#endif /* CONFIG_MBO */ ++ ++#endif /* MBO_AP_H */ +--- contrib/wpa/src/ap/ndisc_snoop.c.orig ++++ contrib/wpa/src/ap/ndisc_snoop.c +@@ -17,6 +17,7 @@ + #include "ap_drv_ops.h" + #include "list.h" + #include "x_snoop.h" ++#include "ndisc_snoop.h" + + struct ip6addr { + struct in6_addr addr; +@@ -181,4 +182,5 @@ + void ndisc_snoop_deinit(struct hostapd_data *hapd) + { + l2_packet_deinit(hapd->sock_ndisc); ++ hapd->sock_ndisc = NULL; + } +--- contrib/wpa/src/ap/neighbor_db.c.orig ++++ contrib/wpa/src/ap/neighbor_db.c +@@ -0,0 +1,257 @@ ++/* ++ * hostapd / Neighboring APs DB ++ * Copyright(c) 2013 - 2016 Intel Mobile Communications GmbH. ++ * Copyright(c) 2011 - 2016 Intel Corporation. All rights reserved. ++ * ++ * 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 "hostapd.h" ++#include "ieee802_11.h" ++#include "neighbor_db.h" ++ ++ ++struct hostapd_neighbor_entry * ++hostapd_neighbor_get(struct hostapd_data *hapd, const u8 *bssid, ++ const struct wpa_ssid_value *ssid) ++{ ++ struct hostapd_neighbor_entry *nr; ++ ++ dl_list_for_each(nr, &hapd->nr_db, struct hostapd_neighbor_entry, ++ list) { ++ if (os_memcmp(bssid, nr->bssid, ETH_ALEN) == 0 && ++ (!ssid || ++ (ssid->ssid_len == nr->ssid.ssid_len && ++ os_memcmp(ssid->ssid, nr->ssid.ssid, ++ ssid->ssid_len) == 0))) ++ return nr; ++ } ++ return NULL; ++} ++ ++ ++static void hostapd_neighbor_clear_entry(struct hostapd_neighbor_entry *nr) ++{ ++ wpabuf_free(nr->nr); ++ nr->nr = NULL; ++ wpabuf_free(nr->lci); ++ nr->lci = NULL; ++ wpabuf_free(nr->civic); ++ nr->civic = NULL; ++ os_memset(nr->bssid, 0, sizeof(nr->bssid)); ++ os_memset(&nr->ssid, 0, sizeof(nr->ssid)); ++ nr->stationary = 0; ++} ++ ++ ++static struct hostapd_neighbor_entry * ++hostapd_neighbor_add(struct hostapd_data *hapd) ++{ ++ struct hostapd_neighbor_entry *nr; ++ ++ nr = os_zalloc(sizeof(struct hostapd_neighbor_entry)); ++ if (!nr) ++ return NULL; ++ ++ dl_list_add(&hapd->nr_db, &nr->list); ++ ++ return nr; ++} ++ ++ ++int hostapd_neighbor_set(struct hostapd_data *hapd, const u8 *bssid, ++ const struct wpa_ssid_value *ssid, ++ const struct wpabuf *nr, const struct wpabuf *lci, ++ const struct wpabuf *civic, int stationary) ++{ ++ struct hostapd_neighbor_entry *entry; ++ ++ entry = hostapd_neighbor_get(hapd, bssid, ssid); ++ if (!entry) ++ entry = hostapd_neighbor_add(hapd); ++ if (!entry) ++ return -1; ++ ++ hostapd_neighbor_clear_entry(entry); ++ ++ os_memcpy(entry->bssid, bssid, ETH_ALEN); ++ os_memcpy(&entry->ssid, ssid, sizeof(entry->ssid)); ++ ++ entry->nr = wpabuf_dup(nr); ++ if (!entry->nr) ++ goto fail; ++ ++ if (lci && wpabuf_len(lci)) { ++ entry->lci = wpabuf_dup(lci); ++ if (!entry->lci || os_get_time(&entry->lci_date)) ++ goto fail; ++ } ++ ++ if (civic && wpabuf_len(civic)) { ++ entry->civic = wpabuf_dup(civic); ++ if (!entry->civic) ++ goto fail; ++ } ++ ++ entry->stationary = stationary; ++ ++ return 0; ++ ++fail: ++ hostapd_neighbor_remove(hapd, bssid, ssid); ++ return -1; ++} ++ ++ ++int hostapd_neighbor_remove(struct hostapd_data *hapd, const u8 *bssid, ++ const struct wpa_ssid_value *ssid) ++{ ++ struct hostapd_neighbor_entry *nr; ++ ++ nr = hostapd_neighbor_get(hapd, bssid, ssid); ++ if (!nr) ++ return -1; ++ ++ hostapd_neighbor_clear_entry(nr); ++ dl_list_del(&nr->list); ++ os_free(nr); ++ ++ return 0; ++} ++ ++ ++void hostapd_free_neighbor_db(struct hostapd_data *hapd) ++{ ++ struct hostapd_neighbor_entry *nr, *prev; ++ ++ dl_list_for_each_safe(nr, prev, &hapd->nr_db, ++ struct hostapd_neighbor_entry, list) { ++ hostapd_neighbor_clear_entry(nr); ++ dl_list_del(&nr->list); ++ os_free(nr); ++ } ++} ++ ++ ++#ifdef NEED_AP_MLME ++static enum nr_chan_width hostapd_get_nr_chan_width(struct hostapd_data *hapd, ++ int ht, int vht) ++{ ++ if (!ht && !vht) ++ return NR_CHAN_WIDTH_20; ++ if (!hapd->iconf->secondary_channel) ++ return NR_CHAN_WIDTH_20; ++ if (!vht || hapd->iconf->vht_oper_chwidth == VHT_CHANWIDTH_USE_HT) ++ return NR_CHAN_WIDTH_40; ++ if (hapd->iconf->vht_oper_chwidth == VHT_CHANWIDTH_80MHZ) ++ return NR_CHAN_WIDTH_80; ++ if (hapd->iconf->vht_oper_chwidth == VHT_CHANWIDTH_160MHZ) ++ return NR_CHAN_WIDTH_160; ++ if (hapd->iconf->vht_oper_chwidth == VHT_CHANWIDTH_80P80MHZ) ++ return NR_CHAN_WIDTH_80P80; ++ return NR_CHAN_WIDTH_20; ++} ++#endif /* NEED_AP_MLME */ ++ ++ ++void hostapd_neighbor_set_own_report(struct hostapd_data *hapd) ++{ ++#ifdef NEED_AP_MLME ++ u16 capab = hostapd_own_capab_info(hapd); ++ int ht = hapd->iconf->ieee80211n && !hapd->conf->disable_11n; ++ int vht = hapd->iconf->ieee80211ac && !hapd->conf->disable_11ac; ++ struct wpa_ssid_value ssid; ++ u8 channel, op_class; ++ u8 center_freq1_idx = 0, center_freq2_idx = 0; ++ enum nr_chan_width width; ++ u32 bssid_info; ++ struct wpabuf *nr; ++ ++ if (!(hapd->conf->radio_measurements[0] & ++ WLAN_RRM_CAPS_NEIGHBOR_REPORT)) ++ return; ++ ++ bssid_info = 3; /* AP is reachable */ ++ bssid_info |= NEI_REP_BSSID_INFO_SECURITY; /* "same as the AP" */ ++ bssid_info |= NEI_REP_BSSID_INFO_KEY_SCOPE; /* "same as the AP" */ ++ ++ if (capab & WLAN_CAPABILITY_SPECTRUM_MGMT) ++ bssid_info |= NEI_REP_BSSID_INFO_SPECTRUM_MGMT; ++ ++ bssid_info |= NEI_REP_BSSID_INFO_RM; /* RRM is supported */ ++ ++ if (hapd->conf->wmm_enabled) { ++ bssid_info |= NEI_REP_BSSID_INFO_QOS; ++ ++ if (hapd->conf->wmm_uapsd && ++ (hapd->iface->drv_flags & WPA_DRIVER_FLAGS_AP_UAPSD)) ++ bssid_info |= NEI_REP_BSSID_INFO_APSD; ++ } ++ ++ if (ht) { ++ bssid_info |= NEI_REP_BSSID_INFO_HT | ++ NEI_REP_BSSID_INFO_DELAYED_BA; ++ ++ /* VHT bit added in IEEE P802.11-REVmc/D4.3 */ ++ if (vht) ++ bssid_info |= NEI_REP_BSSID_INFO_VHT; ++ } ++ ++ /* TODO: Set NEI_REP_BSSID_INFO_MOBILITY_DOMAIN if MDE is set */ ++ ++ if (ieee80211_freq_to_channel_ext(hapd->iface->freq, ++ hapd->iconf->secondary_channel, ++ hapd->iconf->vht_oper_chwidth, ++ &op_class, &channel) == ++ NUM_HOSTAPD_MODES) ++ return; ++ width = hostapd_get_nr_chan_width(hapd, ht, vht); ++ if (vht) { ++ center_freq1_idx = hapd->iconf->vht_oper_centr_freq_seg0_idx; ++ if (width == NR_CHAN_WIDTH_80P80) ++ center_freq2_idx = ++ hapd->iconf->vht_oper_centr_freq_seg1_idx; ++ } else if (ht) { ++ ieee80211_freq_to_chan(hapd->iface->freq + ++ 10 * hapd->iconf->secondary_channel, ++ ¢er_freq1_idx); ++ } ++ ++ ssid.ssid_len = hapd->conf->ssid.ssid_len; ++ os_memcpy(ssid.ssid, hapd->conf->ssid.ssid, ssid.ssid_len); ++ ++ /* ++ * Neighbor Report element size = BSSID + BSSID info + op_class + chan + ++ * phy type + wide bandwidth channel subelement. ++ */ ++ nr = wpabuf_alloc(ETH_ALEN + 4 + 1 + 1 + 1 + 5); ++ if (!nr) ++ return; ++ ++ wpabuf_put_data(nr, hapd->own_addr, ETH_ALEN); ++ wpabuf_put_le32(nr, bssid_info); ++ wpabuf_put_u8(nr, op_class); ++ wpabuf_put_u8(nr, channel); ++ wpabuf_put_u8(nr, ieee80211_get_phy_type(hapd->iface->freq, ht, vht)); ++ ++ /* ++ * Wide Bandwidth Channel subelement may be needed to allow the ++ * receiving STA to send packets to the AP. See IEEE P802.11-REVmc/D5.0 ++ * Figure 9-301. ++ */ ++ wpabuf_put_u8(nr, WNM_NEIGHBOR_WIDE_BW_CHAN); ++ wpabuf_put_u8(nr, 3); ++ wpabuf_put_u8(nr, width); ++ wpabuf_put_u8(nr, center_freq1_idx); ++ wpabuf_put_u8(nr, center_freq2_idx); ++ ++ hostapd_neighbor_set(hapd, hapd->own_addr, &ssid, nr, hapd->iconf->lci, ++ hapd->iconf->civic, hapd->iconf->stationary_ap); ++ ++ wpabuf_free(nr); ++#endif /* NEED_AP_MLME */ ++} +--- contrib/wpa/src/ap/neighbor_db.h.orig ++++ contrib/wpa/src/ap/neighbor_db.h +@@ -0,0 +1,25 @@ ++/* ++ * hostapd / Neighboring APs DB ++ * Copyright(c) 2013 - 2016 Intel Mobile Communications GmbH. ++ * Copyright(c) 2011 - 2016 Intel Corporation. All rights reserved. ++ * ++ * This software may be distributed under the terms of the BSD license. ++ * See README for more details. ++ */ ++ ++#ifndef NEIGHBOR_DB_H ++#define NEIGHBOR_DB_H ++ ++struct hostapd_neighbor_entry * ++hostapd_neighbor_get(struct hostapd_data *hapd, const u8 *bssid, ++ const struct wpa_ssid_value *ssid); ++int hostapd_neighbor_set(struct hostapd_data *hapd, const u8 *bssid, ++ const struct wpa_ssid_value *ssid, ++ const struct wpabuf *nr, const struct wpabuf *lci, ++ const struct wpabuf *civic, int stationary); ++void hostapd_neighbor_set_own_report(struct hostapd_data *hapd); ++int hostapd_neighbor_remove(struct hostapd_data *hapd, const u8 *bssid, ++ const struct wpa_ssid_value *ssid); ++void hostapd_free_neighbor_db(struct hostapd_data *hapd); ++ ++#endif /* NEIGHBOR_DB_H */ +--- contrib/wpa/src/ap/peerkey_auth.c.orig ++++ contrib/wpa/src/ap/peerkey_auth.c +@@ -1,396 +0,0 @@ +-/* +- * hostapd - PeerKey for Direct Link Setup (DLS) +- * Copyright (c) 2006-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 "utils/eloop.h" +-#include "crypto/sha1.h" +-#include "crypto/sha256.h" +-#include "crypto/random.h" +-#include "wpa_auth.h" +-#include "wpa_auth_i.h" +-#include "wpa_auth_ie.h" +- +-#ifdef CONFIG_PEERKEY +- +-static void wpa_stsl_step(void *eloop_ctx, void *timeout_ctx) +-{ +-#if 0 +- struct wpa_authenticator *wpa_auth = eloop_ctx; +- struct wpa_stsl_negotiation *neg = timeout_ctx; +-#endif +- +- /* TODO: ? */ +-} +- +- +-struct wpa_stsl_search { +- const u8 *addr; +- struct wpa_state_machine *sm; +-}; +- +- +-static int wpa_stsl_select_sta(struct wpa_state_machine *sm, void *ctx) +-{ +- struct wpa_stsl_search *search = ctx; +- if (os_memcmp(search->addr, sm->addr, ETH_ALEN) == 0) { +- search->sm = sm; +- return 1; +- } +- return 0; +-} +- +- +-static void wpa_smk_send_error(struct wpa_authenticator *wpa_auth, +- struct wpa_state_machine *sm, const u8 *peer, +- u16 mui, u16 error_type) +-{ +- u8 kde[2 + RSN_SELECTOR_LEN + ETH_ALEN + +- 2 + RSN_SELECTOR_LEN + sizeof(struct rsn_error_kde)]; +- u8 *pos; +- struct rsn_error_kde error; +- +- wpa_auth_logger(wpa_auth, sm->addr, LOGGER_DEBUG, +- "Sending SMK Error"); +- +- pos = kde; +- +- if (peer) { +- pos = wpa_add_kde(pos, RSN_KEY_DATA_MAC_ADDR, peer, ETH_ALEN, +- NULL, 0); +- } +- +- error.mui = host_to_be16(mui); +- error.error_type = host_to_be16(error_type); +- pos = wpa_add_kde(pos, RSN_KEY_DATA_ERROR, +- (u8 *) &error, sizeof(error), NULL, 0); +- +- __wpa_send_eapol(wpa_auth, sm, +- WPA_KEY_INFO_SECURE | WPA_KEY_INFO_MIC | +- WPA_KEY_INFO_SMK_MESSAGE | WPA_KEY_INFO_ERROR, +- NULL, NULL, kde, pos - kde, 0, 0, 0); +-} +- +- +-void wpa_smk_m1(struct wpa_authenticator *wpa_auth, +- struct wpa_state_machine *sm, struct wpa_eapol_key *key, +- const u8 *key_data, size_t key_data_len) +-{ +- struct wpa_eapol_ie_parse kde; +- struct wpa_stsl_search search; +- u8 *buf, *pos; +- size_t buf_len; +- +- if (wpa_parse_kde_ies(key_data, key_data_len, &kde) < 0) { +- wpa_printf(MSG_INFO, "RSN: Failed to parse KDEs in SMK M1"); +- return; +- } +- +- if (kde.rsn_ie == NULL || kde.mac_addr == NULL || +- kde.mac_addr_len < ETH_ALEN) { +- wpa_printf(MSG_INFO, "RSN: No RSN IE or MAC address KDE in " +- "SMK M1"); +- return; +- } +- +- /* Initiator = sm->addr; Peer = kde.mac_addr */ +- +- search.addr = kde.mac_addr; +- search.sm = NULL; +- if (wpa_auth_for_each_sta(wpa_auth, wpa_stsl_select_sta, &search) == +- 0 || search.sm == NULL) { +- wpa_printf(MSG_DEBUG, "RSN: SMK handshake with " MACSTR +- " aborted - STA not associated anymore", +- MAC2STR(kde.mac_addr)); +- wpa_smk_send_error(wpa_auth, sm, kde.mac_addr, STK_MUI_SMK, +- STK_ERR_STA_NR); +- /* FIX: wpa_stsl_remove(wpa_auth, neg); */ +- return; +- } +- +- buf_len = kde.rsn_ie_len + 2 + RSN_SELECTOR_LEN + ETH_ALEN; +- buf = os_malloc(buf_len); +- if (buf == NULL) +- return; +- /* Initiator RSN IE */ +- os_memcpy(buf, kde.rsn_ie, kde.rsn_ie_len); +- pos = buf + kde.rsn_ie_len; +- /* Initiator MAC Address */ +- pos = wpa_add_kde(pos, RSN_KEY_DATA_MAC_ADDR, sm->addr, ETH_ALEN, +- NULL, 0); +- +- /* SMK M2: +- * EAPOL-Key(S=1, M=1, A=1, I=0, K=0, SM=1, KeyRSC=0, Nonce=INonce, +- * MIC=MIC, DataKDs=(RSNIE_I, MAC_I KDE) +- */ +- +- wpa_auth_logger(wpa_auth, search.sm->addr, LOGGER_DEBUG, +- "Sending SMK M2"); +- +- __wpa_send_eapol(wpa_auth, search.sm, +- WPA_KEY_INFO_SECURE | WPA_KEY_INFO_MIC | +- WPA_KEY_INFO_ACK | WPA_KEY_INFO_SMK_MESSAGE, +- NULL, key->key_nonce, buf, pos - buf, 0, 0, 0); +- +- os_free(buf); +-} +- +- +-static void wpa_send_smk_m4(struct wpa_authenticator *wpa_auth, +- struct wpa_state_machine *sm, +- struct wpa_eapol_key *key, +- struct wpa_eapol_ie_parse *kde, +- const u8 *smk) +-{ +- u8 *buf, *pos; +- size_t buf_len; +- u32 lifetime; +- +- /* SMK M4: +- * EAPOL-Key(S=1, M=1, A=0, I=1, K=0, SM=1, KeyRSC=0, Nonce=PNonce, +- * MIC=MIC, DataKDs=(MAC_I KDE, INonce KDE, SMK KDE, +- * Lifetime KDE) +- */ +- +- buf_len = 2 + RSN_SELECTOR_LEN + ETH_ALEN + +- 2 + RSN_SELECTOR_LEN + WPA_NONCE_LEN + +- 2 + RSN_SELECTOR_LEN + PMK_LEN + WPA_NONCE_LEN + +- 2 + RSN_SELECTOR_LEN + sizeof(lifetime); +- pos = buf = os_malloc(buf_len); +- if (buf == NULL) +- return; +- +- /* Initiator MAC Address */ +- pos = wpa_add_kde(pos, RSN_KEY_DATA_MAC_ADDR, kde->mac_addr, ETH_ALEN, +- NULL, 0); +- +- /* Initiator Nonce */ +- pos = wpa_add_kde(pos, RSN_KEY_DATA_NONCE, kde->nonce, WPA_NONCE_LEN, +- NULL, 0); +- +- /* SMK with PNonce */ +- pos = wpa_add_kde(pos, RSN_KEY_DATA_SMK, smk, PMK_LEN, +- key->key_nonce, WPA_NONCE_LEN); +- +- /* Lifetime */ +- lifetime = htonl(43200); /* dot11RSNAConfigSMKLifetime */ +- pos = wpa_add_kde(pos, RSN_KEY_DATA_LIFETIME, +- (u8 *) &lifetime, sizeof(lifetime), NULL, 0); +- +- wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG, +- "Sending SMK M4"); +- +- __wpa_send_eapol(wpa_auth, sm, +- WPA_KEY_INFO_SECURE | WPA_KEY_INFO_MIC | +- WPA_KEY_INFO_INSTALL | WPA_KEY_INFO_SMK_MESSAGE, +- NULL, key->key_nonce, buf, pos - buf, 0, 1, 0); +- +- os_free(buf); +-} +- +- +-static void wpa_send_smk_m5(struct wpa_authenticator *wpa_auth, +- struct wpa_state_machine *sm, +- struct wpa_eapol_key *key, +- struct wpa_eapol_ie_parse *kde, +- const u8 *smk, const u8 *peer) +-{ +- u8 *buf, *pos; +- size_t buf_len; +- u32 lifetime; +- +- /* SMK M5: +- * EAPOL-Key(S=1, M=1, A=0, I=0, K=0, SM=1, KeyRSC=0, Nonce=INonce, +- * MIC=MIC, DataKDs=(RSNIE_P, MAC_P KDE, PNonce, SMK KDE, +- * Lifetime KDE)) +- */ +- +- buf_len = kde->rsn_ie_len + +- 2 + RSN_SELECTOR_LEN + ETH_ALEN + +- 2 + RSN_SELECTOR_LEN + WPA_NONCE_LEN + +- 2 + RSN_SELECTOR_LEN + PMK_LEN + WPA_NONCE_LEN + +- 2 + RSN_SELECTOR_LEN + sizeof(lifetime); +- pos = buf = os_malloc(buf_len); +- if (buf == NULL) +- return; +- +- /* Peer RSN IE */ +- os_memcpy(pos, kde->rsn_ie, kde->rsn_ie_len); +- pos += kde->rsn_ie_len; +- +- /* Peer MAC Address */ +- pos = wpa_add_kde(pos, RSN_KEY_DATA_MAC_ADDR, peer, ETH_ALEN, NULL, 0); +- +- /* PNonce */ +- pos = wpa_add_kde(pos, RSN_KEY_DATA_NONCE, key->key_nonce, +- WPA_NONCE_LEN, NULL, 0); +- +- /* SMK and INonce */ +- pos = wpa_add_kde(pos, RSN_KEY_DATA_SMK, smk, PMK_LEN, +- kde->nonce, WPA_NONCE_LEN); +- +- /* Lifetime */ +- lifetime = htonl(43200); /* dot11RSNAConfigSMKLifetime */ +- pos = wpa_add_kde(pos, RSN_KEY_DATA_LIFETIME, +- (u8 *) &lifetime, sizeof(lifetime), NULL, 0); +- +- wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG, +- "Sending SMK M5"); +- +- __wpa_send_eapol(wpa_auth, sm, +- WPA_KEY_INFO_SECURE | WPA_KEY_INFO_MIC | +- WPA_KEY_INFO_SMK_MESSAGE, +- NULL, kde->nonce, buf, pos - buf, 0, 1, 0); +- +- os_free(buf); +-} +- +- +-void wpa_smk_m3(struct wpa_authenticator *wpa_auth, +- struct wpa_state_machine *sm, struct wpa_eapol_key *key, +- const u8 *key_data, size_t key_data_len) +-{ +- struct wpa_eapol_ie_parse kde; +- struct wpa_stsl_search search; +- u8 smk[32], buf[ETH_ALEN + 8 + 2 * WPA_NONCE_LEN], *pos; +- +- if (wpa_parse_kde_ies(key_data, key_data_len, &kde) < 0) { +- wpa_printf(MSG_INFO, "RSN: Failed to parse KDEs in SMK M3"); +- return; +- } +- +- if (kde.rsn_ie == NULL || +- kde.mac_addr == NULL || kde.mac_addr_len < ETH_ALEN || +- kde.nonce == NULL || kde.nonce_len < WPA_NONCE_LEN) { +- wpa_printf(MSG_INFO, "RSN: No RSN IE, MAC address KDE, or " +- "Nonce KDE in SMK M3"); +- return; +- } +- +- /* Peer = sm->addr; Initiator = kde.mac_addr; +- * Peer Nonce = key->key_nonce; Initiator Nonce = kde.nonce */ +- +- search.addr = kde.mac_addr; +- search.sm = NULL; +- if (wpa_auth_for_each_sta(wpa_auth, wpa_stsl_select_sta, &search) == +- 0 || search.sm == NULL) { +- wpa_printf(MSG_DEBUG, "RSN: SMK handshake with " MACSTR +- " aborted - STA not associated anymore", +- MAC2STR(kde.mac_addr)); +- wpa_smk_send_error(wpa_auth, sm, kde.mac_addr, STK_MUI_SMK, +- STK_ERR_STA_NR); +- /* FIX: wpa_stsl_remove(wpa_auth, neg); */ +- return; +- } +- +- if (random_get_bytes(smk, PMK_LEN)) { +- wpa_printf(MSG_DEBUG, "RSN: Failed to generate SMK"); +- return; +- } +- +- /* SMK = PRF-256(Random number, "SMK Derivation", +- * AA || Time || INonce || PNonce) +- */ +- os_memcpy(buf, wpa_auth->addr, ETH_ALEN); +- pos = buf + ETH_ALEN; +- wpa_get_ntp_timestamp(pos); +- pos += 8; +- os_memcpy(pos, kde.nonce, WPA_NONCE_LEN); +- pos += WPA_NONCE_LEN; +- os_memcpy(pos, key->key_nonce, WPA_NONCE_LEN); +-#ifdef CONFIG_IEEE80211W +- sha256_prf(smk, PMK_LEN, "SMK Derivation", buf, sizeof(buf), +- smk, PMK_LEN); +-#else /* CONFIG_IEEE80211W */ +- sha1_prf(smk, PMK_LEN, "SMK Derivation", buf, sizeof(buf), +- smk, PMK_LEN); +-#endif /* CONFIG_IEEE80211W */ +- +- wpa_hexdump_key(MSG_DEBUG, "RSN: SMK", smk, PMK_LEN); +- +- wpa_send_smk_m4(wpa_auth, sm, key, &kde, smk); +- wpa_send_smk_m5(wpa_auth, search.sm, key, &kde, smk, sm->addr); +- +- /* Authenticator does not need SMK anymore and it is required to forget +- * it. */ +- os_memset(smk, 0, sizeof(*smk)); +-} +- +- +-void wpa_smk_error(struct wpa_authenticator *wpa_auth, +- struct wpa_state_machine *sm, +- const u8 *key_data, size_t key_data_len) +-{ +- struct wpa_eapol_ie_parse kde; +- struct wpa_stsl_search search; +- struct rsn_error_kde error; +- u16 mui, error_type; +- +- if (wpa_parse_kde_ies(key_data, key_data_len, &kde) < 0) { +- wpa_printf(MSG_INFO, "RSN: Failed to parse KDEs in SMK Error"); +- return; +- } +- +- if (kde.mac_addr == NULL || kde.mac_addr_len < ETH_ALEN || +- kde.error == NULL || kde.error_len < sizeof(error)) { +- wpa_printf(MSG_INFO, "RSN: No MAC address or Error KDE in " +- "SMK Error"); +- return; +- } +- +- search.addr = kde.mac_addr; +- search.sm = NULL; +- if (wpa_auth_for_each_sta(wpa_auth, wpa_stsl_select_sta, &search) == +- 0 || search.sm == NULL) { +- wpa_printf(MSG_DEBUG, "RSN: Peer STA " MACSTR " not " +- "associated for SMK Error message from " MACSTR, +- MAC2STR(kde.mac_addr), MAC2STR(sm->addr)); +- return; +- } +- +- os_memcpy(&error, kde.error, sizeof(error)); +- mui = be_to_host16(error.mui); +- error_type = be_to_host16(error.error_type); +- wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_INFO, +- "STA reported SMK Error: Peer " MACSTR +- " MUI %d Error Type %d", +- MAC2STR(kde.mac_addr), mui, error_type); +- +- wpa_smk_send_error(wpa_auth, search.sm, sm->addr, mui, error_type); +-} +- +- +-int wpa_stsl_remove(struct wpa_authenticator *wpa_auth, +- struct wpa_stsl_negotiation *neg) +-{ +- struct wpa_stsl_negotiation *pos, *prev; +- +- if (wpa_auth == NULL) +- return -1; +- pos = wpa_auth->stsl_negotiations; +- prev = NULL; +- while (pos) { +- if (pos == neg) { +- if (prev) +- prev->next = pos->next; +- else +- wpa_auth->stsl_negotiations = pos->next; +- +- eloop_cancel_timeout(wpa_stsl_step, wpa_auth, pos); +- os_free(pos); +- return 0; +- } +- prev = pos; +- pos = pos->next; +- } +- +- return -1; +-} +- +-#endif /* CONFIG_PEERKEY */ +--- contrib/wpa/src/ap/pmksa_cache_auth.c.orig ++++ contrib/wpa/src/ap/pmksa_cache_auth.c +@@ -38,6 +38,7 @@ + + static void _pmksa_cache_free_entry(struct rsn_pmksa_cache_entry *entry) + { ++ os_free(entry->vlan_desc); + os_free(entry->identity); + wpabuf_free(entry->cui); + #ifndef CONFIG_NO_RADIUS +@@ -91,6 +92,20 @@ + } + + ++/** ++ * pmksa_cache_auth_flush - Flush all PMKSA cache entries ++ * @pmksa: Pointer to PMKSA cache data from pmksa_cache_auth_init() ++ */ ++void pmksa_cache_auth_flush(struct rsn_pmksa_cache *pmksa) ++{ ++ while (pmksa->pmksa) { ++ wpa_printf(MSG_DEBUG, "RSN: Flush PMKSA cache entry for " ++ MACSTR, MAC2STR(pmksa->pmksa->spa)); ++ pmksa_cache_free_entry(pmksa, pmksa->pmksa); ++ } ++} ++ ++ + static void pmksa_cache_expire(void *eloop_ctx, void *timeout_ctx) + { + struct rsn_pmksa_cache *pmksa = eloop_ctx; +@@ -126,6 +141,8 @@ + static void pmksa_cache_from_eapol_data(struct rsn_pmksa_cache_entry *entry, + struct eapol_state_machine *eapol) + { ++ struct vlan_description *vlan_desc; ++ + if (eapol == NULL) + return; + +@@ -146,14 +163,22 @@ + #endif /* CONFIG_NO_RADIUS */ + + entry->eap_type_authsrv = eapol->eap_type_authsrv; +- entry->vlan_id = ((struct sta_info *) eapol->sta)->vlan_id; + +- entry->acct_multi_session_id_hi = eapol->acct_multi_session_id_hi; +- entry->acct_multi_session_id_lo = eapol->acct_multi_session_id_lo; ++ vlan_desc = ((struct sta_info *) eapol->sta)->vlan_desc; ++ if (vlan_desc && vlan_desc->notempty) { ++ entry->vlan_desc = os_zalloc(sizeof(struct vlan_description)); ++ if (entry->vlan_desc) ++ *entry->vlan_desc = *vlan_desc; ++ } else { ++ entry->vlan_desc = NULL; ++ } ++ ++ entry->acct_multi_session_id = eapol->acct_multi_session_id; + } + + +-void pmksa_cache_to_eapol_data(struct rsn_pmksa_cache_entry *entry, ++void pmksa_cache_to_eapol_data(struct hostapd_data *hapd, ++ struct rsn_pmksa_cache_entry *entry, + struct eapol_state_machine *eapol) + { + if (entry == NULL || eapol == NULL) +@@ -186,10 +211,11 @@ + } + + eapol->eap_type_authsrv = entry->eap_type_authsrv; +- ((struct sta_info *) eapol->sta)->vlan_id = entry->vlan_id; ++#ifndef CONFIG_NO_VLAN ++ ap_sta_set_vlan(hapd, eapol->sta, entry->vlan_desc); ++#endif /* CONFIG_NO_VLAN */ + +- eapol->acct_multi_session_id_hi = entry->acct_multi_session_id_hi; +- eapol->acct_multi_session_id_lo = entry->acct_multi_session_id_lo; ++ eapol->acct_multi_session_id = entry->acct_multi_session_id; + } + + +@@ -234,6 +260,7 @@ + * @pmksa: Pointer to PMKSA cache data from pmksa_cache_auth_init() + * @pmk: The new pairwise master key + * @pmk_len: PMK length in bytes, usually PMK_LEN (32) ++ * @pmkid: Calculated PMKID + * @kck: Key confirmation key or %NULL if not yet derived + * @kck_len: KCK length in bytes + * @aa: Authenticator address +@@ -250,15 +277,50 @@ + */ + struct rsn_pmksa_cache_entry * + pmksa_cache_auth_add(struct rsn_pmksa_cache *pmksa, +- const u8 *pmk, size_t pmk_len, ++ const u8 *pmk, size_t pmk_len, const u8 *pmkid, + const u8 *kck, size_t kck_len, + const u8 *aa, const u8 *spa, int session_timeout, + struct eapol_state_machine *eapol, int akmp) + { +- struct rsn_pmksa_cache_entry *entry, *pos; ++ struct rsn_pmksa_cache_entry *entry; ++ ++ entry = pmksa_cache_auth_create_entry(pmk, pmk_len, pmkid, kck, kck_len, ++ aa, spa, session_timeout, eapol, ++ akmp); ++ ++ if (pmksa_cache_auth_add_entry(pmksa, entry) < 0) ++ return NULL; ++ ++ return entry; ++} ++ ++ ++/** ++ * pmksa_cache_auth_create_entry - Create a PMKSA cache entry ++ * @pmk: The new pairwise master key ++ * @pmk_len: PMK length in bytes, usually PMK_LEN (32) ++ * @pmkid: Calculated PMKID ++ * @kck: Key confirmation key or %NULL if not yet derived ++ * @kck_len: KCK length in bytes ++ * @aa: Authenticator address ++ * @spa: Supplicant address ++ * @session_timeout: Session timeout ++ * @eapol: Pointer to EAPOL state machine data ++ * @akmp: WPA_KEY_MGMT_* used in key derivation ++ * Returns: Pointer to the added PMKSA cache entry or %NULL on error ++ * ++ * This function creates a PMKSA entry. ++ */ ++struct rsn_pmksa_cache_entry * ++pmksa_cache_auth_create_entry(const u8 *pmk, size_t pmk_len, const u8 *pmkid, ++ const u8 *kck, size_t kck_len, const u8 *aa, ++ const u8 *spa, int session_timeout, ++ struct eapol_state_machine *eapol, int akmp) ++{ ++ struct rsn_pmksa_cache_entry *entry; + struct os_reltime now; + +- if (pmk_len > PMK_LEN) ++ if (pmk_len > PMK_LEN_MAX) + return NULL; + + if (wpa_key_mgmt_suite_b(akmp) && !kck) +@@ -269,13 +331,14 @@ + return NULL; + os_memcpy(entry->pmk, pmk, pmk_len); + entry->pmk_len = pmk_len; +- if (akmp == WPA_KEY_MGMT_IEEE8021X_SUITE_B_192) ++ if (pmkid) ++ os_memcpy(entry->pmkid, pmkid, PMKID_LEN); ++ else if (akmp == WPA_KEY_MGMT_IEEE8021X_SUITE_B_192) + rsn_pmkid_suite_b_192(kck, kck_len, aa, spa, entry->pmkid); + else if (wpa_key_mgmt_suite_b(akmp)) + rsn_pmkid_suite_b(kck, kck_len, aa, spa, entry->pmkid); + else +- rsn_pmkid(pmk, pmk_len, aa, spa, entry->pmkid, +- wpa_key_mgmt_sha256(akmp)); ++ rsn_pmkid(pmk, pmk_len, aa, spa, entry->pmkid, akmp); + os_get_reltime(&now); + entry->expiration = now.sec; + if (session_timeout > 0) +@@ -286,9 +349,30 @@ + os_memcpy(entry->spa, spa, ETH_ALEN); + pmksa_cache_from_eapol_data(entry, eapol); + ++ return entry; ++} ++ ++ ++/** ++ * pmksa_cache_auth_add_entry - Add a PMKSA cache entry ++ * @pmksa: Pointer to PMKSA cache data from pmksa_cache_auth_init() ++ * @entry: Pointer to PMKSA cache entry ++ * ++ * This function adds PMKSA cache entry to the PMKSA cache. If an old entry is ++ * already in the cache for the same Supplicant, this entry will be replaced ++ * with the new entry. PMKID will be calculated based on the PMK. ++ */ ++int pmksa_cache_auth_add_entry(struct rsn_pmksa_cache *pmksa, ++ struct rsn_pmksa_cache_entry *entry) ++{ ++ struct rsn_pmksa_cache_entry *pos; ++ ++ if (entry == NULL) ++ return -1; ++ + /* Replace an old entry for the same STA (if found) with the new entry + */ +- pos = pmksa_cache_auth_get(pmksa, spa, NULL); ++ pos = pmksa_cache_auth_get(pmksa, entry->spa, NULL); + if (pos) + pmksa_cache_free_entry(pmksa, pos); + +@@ -302,7 +386,7 @@ + + pmksa_cache_link_entry(pmksa, entry); + +- return entry; ++ return 0; + } + + +@@ -337,7 +421,13 @@ + radius_copy_class(&entry->radius_class, &old_entry->radius_class); + #endif /* CONFIG_NO_RADIUS */ + entry->eap_type_authsrv = old_entry->eap_type_authsrv; +- entry->vlan_id = old_entry->vlan_id; ++ if (old_entry->vlan_desc) { ++ entry->vlan_desc = os_zalloc(sizeof(struct vlan_description)); ++ if (entry->vlan_desc) ++ *entry->vlan_desc = *old_entry->vlan_desc; ++ } else { ++ entry->vlan_desc = NULL; ++ } + entry->opportunistic = 1; + + pmksa_cache_link_entry(pmksa, entry); +@@ -427,7 +517,7 @@ + if (os_memcmp(entry->spa, spa, ETH_ALEN) != 0) + continue; + rsn_pmkid(entry->pmk, entry->pmk_len, aa, spa, new_pmkid, +- wpa_key_mgmt_sha256(entry->akmp)); ++ entry->akmp); + if (os_memcmp(new_pmkid, pmkid, PMKID_LEN) == 0) + return entry; + } +@@ -471,12 +561,11 @@ + if (attr->acct_multi_session_id) { + char buf[20]; + +- if (attr->acct_multi_session_id_len != 17) ++ if (attr->acct_multi_session_id_len != 16) + return 0; +- os_snprintf(buf, sizeof(buf), "%08X+%08X", +- entry->acct_multi_session_id_hi, +- entry->acct_multi_session_id_lo); +- if (os_memcmp(attr->acct_multi_session_id, buf, 17) != 0) ++ os_snprintf(buf, sizeof(buf), "%016llX", ++ (unsigned long long) entry->acct_multi_session_id); ++ if (os_memcmp(attr->acct_multi_session_id, buf, 16) != 0) + return 0; + match++; + } +@@ -526,3 +615,115 @@ + + return found ? 0 : -1; + } ++ ++ ++/** ++ * pmksa_cache_auth_list - Dump text list of entries in PMKSA cache ++ * @pmksa: Pointer to PMKSA cache data from pmksa_cache_auth_init() ++ * @buf: Buffer for the list ++ * @len: Length of the buffer ++ * Returns: Number of bytes written to buffer ++ * ++ * This function is used to generate a text format representation of the ++ * current PMKSA cache contents for the ctrl_iface PMKSA command. ++ */ ++int pmksa_cache_auth_list(struct rsn_pmksa_cache *pmksa, char *buf, size_t len) ++{ ++ int i, ret; ++ char *pos = buf; ++ struct rsn_pmksa_cache_entry *entry; ++ struct os_reltime now; ++ ++ os_get_reltime(&now); ++ ret = os_snprintf(pos, buf + len - pos, ++ "Index / SPA / PMKID / expiration (in seconds) / opportunistic\n"); ++ if (os_snprintf_error(buf + len - pos, ret)) ++ return pos - buf; ++ pos += ret; ++ i = 0; ++ entry = pmksa->pmksa; ++ while (entry) { ++ ret = os_snprintf(pos, buf + len - pos, "%d " MACSTR " ", ++ i, MAC2STR(entry->spa)); ++ if (os_snprintf_error(buf + len - pos, ret)) ++ return pos - buf; ++ pos += ret; ++ pos += wpa_snprintf_hex(pos, buf + len - pos, entry->pmkid, ++ PMKID_LEN); ++ ret = os_snprintf(pos, buf + len - pos, " %d %d\n", ++ (int) (entry->expiration - now.sec), ++ entry->opportunistic); ++ if (os_snprintf_error(buf + len - pos, ret)) ++ return pos - buf; ++ pos += ret; ++ entry = entry->next; ++ } ++ return pos - buf; ++} ++ ++ ++#ifdef CONFIG_PMKSA_CACHE_EXTERNAL ++#ifdef CONFIG_MESH ++ ++/** ++ * pmksa_cache_auth_list_mesh - Dump text list of entries in PMKSA cache ++ * @pmksa: Pointer to PMKSA cache data from pmksa_cache_auth_init() ++ * @addr: MAC address of the peer (NULL means any) ++ * @buf: Buffer for the list ++ * @len: Length of the buffer ++ * Returns: Number of bytes written to buffer ++ * ++ * This function is used to generate a text format representation of the ++ * current PMKSA cache contents for the ctrl_iface PMKSA_GET command to store ++ * in external storage. ++ */ ++int pmksa_cache_auth_list_mesh(struct rsn_pmksa_cache *pmksa, const u8 *addr, ++ char *buf, size_t len) ++{ ++ int ret; ++ char *pos, *end; ++ struct rsn_pmksa_cache_entry *entry; ++ struct os_reltime now; ++ ++ pos = buf; ++ end = buf + len; ++ os_get_reltime(&now); ++ ++ ++ /* ++ * Entry format: ++ * ++ */ ++ for (entry = pmksa->pmksa; entry; entry = entry->next) { ++ if (addr && os_memcmp(entry->spa, addr, ETH_ALEN) != 0) ++ continue; ++ ++ ret = os_snprintf(pos, end - pos, MACSTR " ", ++ MAC2STR(entry->spa)); ++ if (os_snprintf_error(end - pos, ret)) ++ return 0; ++ pos += ret; ++ ++ pos += wpa_snprintf_hex(pos, end - pos, entry->pmkid, ++ PMKID_LEN); ++ ++ ret = os_snprintf(pos, end - pos, " "); ++ if (os_snprintf_error(end - pos, ret)) ++ return 0; ++ pos += ret; ++ ++ pos += wpa_snprintf_hex(pos, end - pos, entry->pmk, ++ entry->pmk_len); ++ ++ ret = os_snprintf(pos, end - pos, " %d\n", ++ (int) (entry->expiration - now.sec)); ++ if (os_snprintf_error(end - pos, ret)) ++ return 0; ++ pos += ret; ++ } ++ ++ return pos - buf; ++} ++ ++#endif /* CONFIG_MESH */ ++#endif /* CONFIG_PMKSA_CACHE_EXTERNAL */ +--- contrib/wpa/src/ap/pmksa_cache_auth.h.orig ++++ contrib/wpa/src/ap/pmksa_cache_auth.h +@@ -17,7 +17,7 @@ + struct rsn_pmksa_cache_entry { + struct rsn_pmksa_cache_entry *next, *hnext; + u8 pmkid[PMKID_LEN]; +- u8 pmk[PMK_LEN]; ++ u8 pmk[PMK_LEN_MAX]; + size_t pmk_len; + os_time_t expiration; + int akmp; /* WPA_KEY_MGMT_* */ +@@ -28,14 +28,14 @@ + struct wpabuf *cui; + struct radius_class_data radius_class; + u8 eap_type_authsrv; +- int vlan_id; ++ struct vlan_description *vlan_desc; + int opportunistic; + +- u32 acct_multi_session_id_hi; +- u32 acct_multi_session_id_lo; ++ u64 acct_multi_session_id; + }; + + struct rsn_pmksa_cache; ++struct radius_das_attrs; + + struct rsn_pmksa_cache * + pmksa_cache_auth_init(void (*free_cb)(struct rsn_pmksa_cache_entry *entry, +@@ -49,19 +49,31 @@ + const u8 *pmkid); + struct rsn_pmksa_cache_entry * + pmksa_cache_auth_add(struct rsn_pmksa_cache *pmksa, +- const u8 *pmk, size_t pmk_len, ++ const u8 *pmk, size_t pmk_len, const u8 *pmkid, + const u8 *kck, size_t kck_len, + const u8 *aa, const u8 *spa, int session_timeout, + struct eapol_state_machine *eapol, int akmp); + struct rsn_pmksa_cache_entry * ++pmksa_cache_auth_create_entry(const u8 *pmk, size_t pmk_len, const u8 *pmkid, ++ const u8 *kck, size_t kck_len, const u8 *aa, ++ const u8 *spa, int session_timeout, ++ struct eapol_state_machine *eapol, int akmp); ++int pmksa_cache_auth_add_entry(struct rsn_pmksa_cache *pmksa, ++ struct rsn_pmksa_cache_entry *entry); ++struct rsn_pmksa_cache_entry * + pmksa_cache_add_okc(struct rsn_pmksa_cache *pmksa, + const struct rsn_pmksa_cache_entry *old_entry, + const u8 *aa, const u8 *pmkid); +-void pmksa_cache_to_eapol_data(struct rsn_pmksa_cache_entry *entry, ++void pmksa_cache_to_eapol_data(struct hostapd_data *hapd, ++ struct rsn_pmksa_cache_entry *entry, + struct eapol_state_machine *eapol); + void pmksa_cache_free_entry(struct rsn_pmksa_cache *pmksa, + struct rsn_pmksa_cache_entry *entry); + int pmksa_cache_auth_radius_das_disconnect(struct rsn_pmksa_cache *pmksa, + struct radius_das_attrs *attr); ++int pmksa_cache_auth_list(struct rsn_pmksa_cache *pmksa, char *buf, size_t len); ++void pmksa_cache_auth_flush(struct rsn_pmksa_cache *pmksa); ++int pmksa_cache_auth_list_mesh(struct rsn_pmksa_cache *pmksa, const u8 *addr, ++ char *buf, size_t len); + + #endif /* PMKSA_CACHE_H */ +--- contrib/wpa/src/ap/rrm.c.orig ++++ contrib/wpa/src/ap/rrm.c +@@ -0,0 +1,674 @@ ++/* ++ * hostapd / Radio Measurement (RRM) ++ * Copyright(c) 2013 - 2016 Intel Mobile Communications GmbH. ++ * Copyright(c) 2011 - 2016 Intel Corporation. All rights reserved. ++ * Copyright (c) 2016-2017, 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/wpa_ctrl.h" ++#include "hostapd.h" ++#include "ap_drv_ops.h" ++#include "sta_info.h" ++#include "eloop.h" ++#include "neighbor_db.h" ++#include "rrm.h" ++ ++#define HOSTAPD_RRM_REQUEST_TIMEOUT 5 ++ ++ ++static void hostapd_lci_rep_timeout_handler(void *eloop_data, void *user_ctx) ++{ ++ struct hostapd_data *hapd = eloop_data; ++ ++ wpa_printf(MSG_DEBUG, "RRM: LCI request (token %u) timed out", ++ hapd->lci_req_token); ++ hapd->lci_req_active = 0; ++} ++ ++ ++static void hostapd_handle_lci_report(struct hostapd_data *hapd, u8 token, ++ const u8 *pos, size_t len) ++{ ++ if (!hapd->lci_req_active || hapd->lci_req_token != token) { ++ wpa_printf(MSG_DEBUG, "Unexpected LCI report, token %u", token); ++ return; ++ } ++ ++ hapd->lci_req_active = 0; ++ eloop_cancel_timeout(hostapd_lci_rep_timeout_handler, hapd, NULL); ++ wpa_printf(MSG_DEBUG, "LCI report token %u len %zu", token, len); ++} ++ ++ ++static void hostapd_range_rep_timeout_handler(void *eloop_data, void *user_ctx) ++{ ++ struct hostapd_data *hapd = eloop_data; ++ ++ wpa_printf(MSG_DEBUG, "RRM: Range request (token %u) timed out", ++ hapd->range_req_token); ++ hapd->range_req_active = 0; ++} ++ ++ ++static void hostapd_handle_range_report(struct hostapd_data *hapd, u8 token, ++ const u8 *pos, size_t len) ++{ ++ if (!hapd->range_req_active || hapd->range_req_token != token) { ++ wpa_printf(MSG_DEBUG, "Unexpected range report, token %u", ++ token); ++ return; ++ } ++ ++ hapd->range_req_active = 0; ++ eloop_cancel_timeout(hostapd_range_rep_timeout_handler, hapd, NULL); ++ wpa_printf(MSG_DEBUG, "Range report token %u len %zu", token, len); ++} ++ ++ ++static void hostapd_handle_beacon_report(struct hostapd_data *hapd, ++ const u8 *addr, u8 token, u8 rep_mode, ++ const u8 *pos, size_t len) ++{ ++ char report[2 * 255 + 1]; ++ ++ wpa_printf(MSG_DEBUG, "Beacon report token %u len %zu from " MACSTR, ++ token, len, MAC2STR(addr)); ++ /* Skip to the beginning of the Beacon report */ ++ if (len < 3) ++ return; ++ pos += 3; ++ len -= 3; ++ report[0] = '\0'; ++ if (wpa_snprintf_hex(report, sizeof(report), pos, len) < 0) ++ return; ++ wpa_msg(hapd->msg_ctx, MSG_INFO, BEACON_RESP_RX MACSTR " %u %02x %s", ++ MAC2STR(addr), token, rep_mode, report); ++} ++ ++ ++static void hostapd_handle_radio_msmt_report(struct hostapd_data *hapd, ++ const u8 *buf, size_t len) ++{ ++ const struct ieee80211_mgmt *mgmt = (const struct ieee80211_mgmt *) buf; ++ const u8 *pos, *ie, *end; ++ u8 token, rep_mode; ++ ++ end = buf + len; ++ token = mgmt->u.action.u.rrm.dialog_token; ++ pos = mgmt->u.action.u.rrm.variable; ++ ++ while ((ie = get_ie(pos, end - pos, WLAN_EID_MEASURE_REPORT))) { ++ if (ie[1] < 3) { ++ wpa_printf(MSG_DEBUG, "Bad Measurement Report element"); ++ break; ++ } ++ ++ rep_mode = ie[3]; ++ wpa_printf(MSG_DEBUG, "Measurement report mode 0x%x type %u", ++ rep_mode, ie[4]); ++ ++ switch (ie[4]) { ++ case MEASURE_TYPE_LCI: ++ hostapd_handle_lci_report(hapd, token, ie + 2, ie[1]); ++ break; ++ case MEASURE_TYPE_FTM_RANGE: ++ hostapd_handle_range_report(hapd, token, ie + 2, ie[1]); ++ break; ++ case MEASURE_TYPE_BEACON: ++ hostapd_handle_beacon_report(hapd, mgmt->sa, token, ++ rep_mode, ie + 2, ie[1]); ++ break; ++ default: ++ wpa_printf(MSG_DEBUG, ++ "Measurement report type %u is not supported", ++ ie[4]); ++ break; ++ } ++ ++ pos = ie + ie[1] + 2; ++ } ++} ++ ++ ++static u16 hostapd_parse_location_lci_req_age(const u8 *buf, size_t len) ++{ ++ const u8 *subelem; ++ ++ /* Range Request element + Location Subject + Maximum Age subelement */ ++ if (len < 3 + 1 + 4) ++ return 0; ++ ++ /* Subelements are arranged as IEs */ ++ subelem = get_ie(buf + 4, len - 4, LCI_REQ_SUBELEM_MAX_AGE); ++ if (subelem && subelem[1] == 2) ++ return WPA_GET_LE16(subelem + 2); ++ ++ return 0; ++} ++ ++ ++static int hostapd_check_lci_age(struct hostapd_neighbor_entry *nr, u16 max_age) ++{ ++ struct os_time curr, diff; ++ unsigned long diff_l; ++ ++ if (nr->stationary || max_age == 0xffff) ++ return 1; ++ ++ if (!max_age) ++ return 0; ++ ++ if (os_get_time(&curr)) ++ return 0; ++ ++ os_time_sub(&curr, &nr->lci_date, &diff); ++ ++ /* avoid overflow */ ++ if (diff.sec > 0xffff) ++ return 0; ++ ++ /* LCI age is calculated in 10th of a second units. */ ++ diff_l = diff.sec * 10 + diff.usec / 100000; ++ ++ return max_age > diff_l; ++} ++ ++ ++static size_t hostapd_neighbor_report_len(struct wpabuf *buf, ++ struct hostapd_neighbor_entry *nr, ++ int send_lci, int send_civic) ++{ ++ size_t len = 2 + wpabuf_len(nr->nr); ++ ++ if (send_lci && nr->lci) ++ len += 2 + wpabuf_len(nr->lci); ++ ++ if (send_civic && nr->civic) ++ len += 2 + wpabuf_len(nr->civic); ++ ++ return len; ++} ++ ++ ++static void hostapd_send_nei_report_resp(struct hostapd_data *hapd, ++ const u8 *addr, u8 dialog_token, ++ struct wpa_ssid_value *ssid, u8 lci, ++ u8 civic, u16 lci_max_age) ++{ ++ struct hostapd_neighbor_entry *nr; ++ struct wpabuf *buf; ++ u8 *msmt_token; ++ ++ /* ++ * The number and length of the Neighbor Report elements in a Neighbor ++ * Report frame is limited by the maximum allowed MMPDU size; + 3 bytes ++ * of RRM header. ++ */ ++ buf = wpabuf_alloc(3 + IEEE80211_MAX_MMPDU_SIZE); ++ if (!buf) ++ return; ++ ++ wpabuf_put_u8(buf, WLAN_ACTION_RADIO_MEASUREMENT); ++ wpabuf_put_u8(buf, WLAN_RRM_NEIGHBOR_REPORT_RESPONSE); ++ wpabuf_put_u8(buf, dialog_token); ++ ++ dl_list_for_each(nr, &hapd->nr_db, struct hostapd_neighbor_entry, ++ list) { ++ int send_lci; ++ size_t len; ++ ++ if (ssid->ssid_len != nr->ssid.ssid_len || ++ os_memcmp(ssid->ssid, nr->ssid.ssid, ssid->ssid_len) != 0) ++ continue; ++ ++ send_lci = (lci != 0) && hostapd_check_lci_age(nr, lci_max_age); ++ len = hostapd_neighbor_report_len(buf, nr, send_lci, civic); ++ ++ if (len - 2 > 0xff) { ++ wpa_printf(MSG_DEBUG, ++ "NR entry for " MACSTR " exceeds 0xFF bytes", ++ MAC2STR(nr->bssid)); ++ continue; ++ } ++ ++ if (len > wpabuf_tailroom(buf)) ++ break; ++ ++ wpabuf_put_u8(buf, WLAN_EID_NEIGHBOR_REPORT); ++ wpabuf_put_u8(buf, len - 2); ++ wpabuf_put_buf(buf, nr->nr); ++ ++ if (send_lci && nr->lci) { ++ wpabuf_put_u8(buf, WLAN_EID_MEASURE_REPORT); ++ wpabuf_put_u8(buf, wpabuf_len(nr->lci)); ++ /* ++ * Override measurement token - the first byte of the ++ * Measurement Report element. ++ */ ++ msmt_token = wpabuf_put(buf, 0); ++ wpabuf_put_buf(buf, nr->lci); ++ *msmt_token = lci; ++ } ++ ++ if (civic && nr->civic) { ++ wpabuf_put_u8(buf, WLAN_EID_MEASURE_REPORT); ++ wpabuf_put_u8(buf, wpabuf_len(nr->civic)); ++ /* ++ * Override measurement token - the first byte of the ++ * Measurement Report element. ++ */ ++ msmt_token = wpabuf_put(buf, 0); ++ wpabuf_put_buf(buf, nr->civic); ++ *msmt_token = civic; ++ } ++ } ++ ++ hostapd_drv_send_action(hapd, hapd->iface->freq, 0, addr, ++ wpabuf_head(buf), wpabuf_len(buf)); ++ wpabuf_free(buf); ++} ++ ++ ++static void hostapd_handle_nei_report_req(struct hostapd_data *hapd, ++ const u8 *buf, size_t len) ++{ ++ const struct ieee80211_mgmt *mgmt = (const struct ieee80211_mgmt *) buf; ++ const u8 *pos, *ie, *end; ++ struct wpa_ssid_value ssid = { ++ .ssid_len = 0 ++ }; ++ u8 token; ++ u8 lci = 0, civic = 0; /* Measurement tokens */ ++ u16 lci_max_age = 0; ++ ++ if (!(hapd->conf->radio_measurements[0] & ++ WLAN_RRM_CAPS_NEIGHBOR_REPORT)) ++ return; ++ ++ end = buf + len; ++ ++ token = mgmt->u.action.u.rrm.dialog_token; ++ pos = mgmt->u.action.u.rrm.variable; ++ len = end - pos; ++ ++ ie = get_ie(pos, len, WLAN_EID_SSID); ++ if (ie && ie[1] && ie[1] <= SSID_MAX_LEN) { ++ ssid.ssid_len = ie[1]; ++ os_memcpy(ssid.ssid, ie + 2, ssid.ssid_len); ++ } else { ++ ssid.ssid_len = hapd->conf->ssid.ssid_len; ++ os_memcpy(ssid.ssid, hapd->conf->ssid.ssid, ssid.ssid_len); ++ } ++ ++ while ((ie = get_ie(pos, len, WLAN_EID_MEASURE_REQUEST))) { ++ if (ie[1] < 3) ++ break; ++ ++ wpa_printf(MSG_DEBUG, ++ "Neighbor report request, measure type %u", ++ ie[4]); ++ ++ switch (ie[4]) { /* Measurement Type */ ++ case MEASURE_TYPE_LCI: ++ lci = ie[2]; /* Measurement Token */ ++ lci_max_age = hostapd_parse_location_lci_req_age(ie + 2, ++ ie[1]); ++ break; ++ case MEASURE_TYPE_LOCATION_CIVIC: ++ civic = ie[2]; /* Measurement token */ ++ break; ++ } ++ ++ pos = ie + ie[1] + 2; ++ len = end - pos; ++ } ++ ++ hostapd_send_nei_report_resp(hapd, mgmt->sa, token, &ssid, lci, civic, ++ lci_max_age); ++} ++ ++ ++void hostapd_handle_radio_measurement(struct hostapd_data *hapd, ++ const u8 *buf, size_t len) ++{ ++ const struct ieee80211_mgmt *mgmt = (const struct ieee80211_mgmt *) buf; ++ ++ /* ++ * Check for enough bytes: header + (1B)Category + (1B)Action + ++ * (1B)Dialog Token. ++ */ ++ if (len < IEEE80211_HDRLEN + 3) ++ return; ++ ++ wpa_printf(MSG_DEBUG, "Radio measurement frame, action %u from " MACSTR, ++ mgmt->u.action.u.rrm.action, MAC2STR(mgmt->sa)); ++ ++ switch (mgmt->u.action.u.rrm.action) { ++ case WLAN_RRM_RADIO_MEASUREMENT_REPORT: ++ hostapd_handle_radio_msmt_report(hapd, buf, len); ++ break; ++ case WLAN_RRM_NEIGHBOR_REPORT_REQUEST: ++ hostapd_handle_nei_report_req(hapd, buf, len); ++ break; ++ default: ++ wpa_printf(MSG_DEBUG, "RRM action %u is not supported", ++ mgmt->u.action.u.rrm.action); ++ break; ++ } ++} ++ ++ ++int hostapd_send_lci_req(struct hostapd_data *hapd, const u8 *addr) ++{ ++ struct wpabuf *buf; ++ struct sta_info *sta = ap_get_sta(hapd, addr); ++ int ret; ++ ++ if (!sta || !(sta->flags & WLAN_STA_AUTHORIZED)) { ++ wpa_printf(MSG_INFO, ++ "Request LCI: Destination address is not connected"); ++ return -1; ++ } ++ ++ if (!(sta->rrm_enabled_capa[1] & WLAN_RRM_CAPS_LCI_MEASUREMENT)) { ++ wpa_printf(MSG_INFO, ++ "Request LCI: Station does not support LCI in RRM"); ++ return -1; ++ } ++ ++ if (hapd->lci_req_active) { ++ wpa_printf(MSG_DEBUG, ++ "Request LCI: LCI request is already in process, overriding"); ++ hapd->lci_req_active = 0; ++ eloop_cancel_timeout(hostapd_lci_rep_timeout_handler, hapd, ++ NULL); ++ } ++ ++ /* Measurement request (5) + Measurement element with LCI (10) */ ++ buf = wpabuf_alloc(5 + 10); ++ if (!buf) ++ return -1; ++ ++ hapd->lci_req_token++; ++ /* For wraparounds - the token must be nonzero */ ++ if (!hapd->lci_req_token) ++ hapd->lci_req_token++; ++ ++ wpabuf_put_u8(buf, WLAN_ACTION_RADIO_MEASUREMENT); ++ wpabuf_put_u8(buf, WLAN_RRM_RADIO_MEASUREMENT_REQUEST); ++ wpabuf_put_u8(buf, hapd->lci_req_token); ++ wpabuf_put_le16(buf, 0); /* Number of repetitions */ ++ ++ wpabuf_put_u8(buf, WLAN_EID_MEASURE_REQUEST); ++ wpabuf_put_u8(buf, 3 + 1 + 4); ++ ++ wpabuf_put_u8(buf, 1); /* Measurement Token */ ++ /* ++ * Parallel and Enable bits are 0, Duration, Request, and Report are ++ * reserved. ++ */ ++ wpabuf_put_u8(buf, 0); ++ wpabuf_put_u8(buf, MEASURE_TYPE_LCI); ++ ++ wpabuf_put_u8(buf, LOCATION_SUBJECT_REMOTE); ++ ++ wpabuf_put_u8(buf, LCI_REQ_SUBELEM_MAX_AGE); ++ wpabuf_put_u8(buf, 2); ++ wpabuf_put_le16(buf, 0xffff); ++ ++ ret = hostapd_drv_send_action(hapd, hapd->iface->freq, 0, addr, ++ wpabuf_head(buf), wpabuf_len(buf)); ++ wpabuf_free(buf); ++ if (ret) ++ return ret; ++ ++ hapd->lci_req_active = 1; ++ ++ eloop_register_timeout(HOSTAPD_RRM_REQUEST_TIMEOUT, 0, ++ hostapd_lci_rep_timeout_handler, hapd, NULL); ++ ++ return 0; ++} ++ ++ ++int hostapd_send_range_req(struct hostapd_data *hapd, const u8 *addr, ++ u16 random_interval, u8 min_ap, ++ const u8 *responders, unsigned int n_responders) ++{ ++ struct wpabuf *buf; ++ struct sta_info *sta; ++ u8 *len; ++ unsigned int i; ++ int ret; ++ ++ wpa_printf(MSG_DEBUG, "Request range: dest addr " MACSTR ++ " rand interval %u min AP %u n_responders %u", MAC2STR(addr), ++ random_interval, min_ap, n_responders); ++ ++ if (min_ap == 0 || min_ap > n_responders) { ++ wpa_printf(MSG_INFO, "Request range: Wrong min AP count"); ++ return -1; ++ } ++ ++ sta = ap_get_sta(hapd, addr); ++ if (!sta || !(sta->flags & WLAN_STA_AUTHORIZED)) { ++ wpa_printf(MSG_INFO, ++ "Request range: Destination address is not connected"); ++ return -1; ++ } ++ ++ if (!(sta->rrm_enabled_capa[4] & WLAN_RRM_CAPS_FTM_RANGE_REPORT)) { ++ wpa_printf(MSG_ERROR, ++ "Request range: Destination station does not support FTM range report in RRM"); ++ return -1; ++ } ++ ++ if (hapd->range_req_active) { ++ wpa_printf(MSG_DEBUG, ++ "Request range: Range request is already in process; overriding"); ++ hapd->range_req_active = 0; ++ eloop_cancel_timeout(hostapd_range_rep_timeout_handler, hapd, ++ NULL); ++ } ++ ++ /* Action + measurement type + token + reps + EID + len = 7 */ ++ buf = wpabuf_alloc(7 + 255); ++ if (!buf) ++ return -1; ++ ++ hapd->range_req_token++; ++ if (!hapd->range_req_token) /* For wraparounds */ ++ hapd->range_req_token++; ++ ++ /* IEEE P802.11-REVmc/D5.0, 9.6.7.2 */ ++ wpabuf_put_u8(buf, WLAN_ACTION_RADIO_MEASUREMENT); ++ wpabuf_put_u8(buf, WLAN_RRM_RADIO_MEASUREMENT_REQUEST); ++ wpabuf_put_u8(buf, hapd->range_req_token); /* Dialog Token */ ++ wpabuf_put_le16(buf, 0); /* Number of Repetitions */ ++ ++ /* IEEE P802.11-REVmc/D5.0, 9.4.2.21 */ ++ wpabuf_put_u8(buf, WLAN_EID_MEASURE_REQUEST); ++ len = wpabuf_put(buf, 1); /* Length will be set later */ ++ ++ wpabuf_put_u8(buf, 1); /* Measurement Token */ ++ /* ++ * Parallel and Enable bits are 0; Duration, Request, and Report are ++ * reserved. ++ */ ++ wpabuf_put_u8(buf, 0); /* Measurement Request Mode */ ++ wpabuf_put_u8(buf, MEASURE_TYPE_FTM_RANGE); /* Measurement Type */ ++ ++ /* IEEE P802.11-REVmc/D5.0, 9.4.2.21.19 */ ++ wpabuf_put_le16(buf, random_interval); /* Randomization Interval */ ++ wpabuf_put_u8(buf, min_ap); /* Minimum AP Count */ ++ ++ /* FTM Range Subelements */ ++ ++ /* ++ * Taking the neighbor report part of the range request from neighbor ++ * database instead of requesting the separate bits of data from the ++ * user. ++ */ ++ for (i = 0; i < n_responders; i++) { ++ struct hostapd_neighbor_entry *nr; ++ ++ nr = hostapd_neighbor_get(hapd, responders + ETH_ALEN * i, ++ NULL); ++ if (!nr) { ++ wpa_printf(MSG_INFO, "Missing neighbor report for " ++ MACSTR, MAC2STR(responders + ETH_ALEN * i)); ++ wpabuf_free(buf); ++ return -1; ++ } ++ ++ if (wpabuf_tailroom(buf) < 2 + wpabuf_len(nr->nr)) { ++ wpa_printf(MSG_ERROR, "Too long range request"); ++ wpabuf_free(buf); ++ return -1; ++ } ++ ++ wpabuf_put_u8(buf, WLAN_EID_NEIGHBOR_REPORT); ++ wpabuf_put_u8(buf, wpabuf_len(nr->nr)); ++ wpabuf_put_buf(buf, nr->nr); ++ } ++ ++ /* Action + measurement type + token + reps + EID + len = 7 */ ++ *len = wpabuf_len(buf) - 7; ++ ++ ret = hostapd_drv_send_action(hapd, hapd->iface->freq, 0, addr, ++ wpabuf_head(buf), wpabuf_len(buf)); ++ wpabuf_free(buf); ++ if (ret) ++ return ret; ++ ++ hapd->range_req_active = 1; ++ ++ eloop_register_timeout(HOSTAPD_RRM_REQUEST_TIMEOUT, 0, ++ hostapd_range_rep_timeout_handler, hapd, NULL); ++ ++ return 0; ++} ++ ++ ++void hostapd_clean_rrm(struct hostapd_data *hapd) ++{ ++ hostapd_free_neighbor_db(hapd); ++ eloop_cancel_timeout(hostapd_lci_rep_timeout_handler, hapd, NULL); ++ hapd->lci_req_active = 0; ++ eloop_cancel_timeout(hostapd_range_rep_timeout_handler, hapd, NULL); ++ hapd->range_req_active = 0; ++} ++ ++ ++int hostapd_send_beacon_req(struct hostapd_data *hapd, const u8 *addr, ++ u8 req_mode, const struct wpabuf *req) ++{ ++ struct wpabuf *buf; ++ struct sta_info *sta = ap_get_sta(hapd, addr); ++ int ret; ++ enum beacon_report_mode mode; ++ const u8 *pos; ++ ++ /* Request data: ++ * Operating Class (1), Channel Number (1), Randomization Interval (2), ++ * Measurement Duration (2), Measurement Mode (1), BSSID (6), ++ * Optional Subelements (variable) ++ */ ++ if (wpabuf_len(req) < 13) { ++ wpa_printf(MSG_INFO, "Beacon request: Too short request data"); ++ return -1; ++ } ++ pos = wpabuf_head(req); ++ mode = pos[6]; ++ ++ if (!sta || !(sta->flags & WLAN_STA_AUTHORIZED)) { ++ wpa_printf(MSG_INFO, ++ "Beacon request: " MACSTR " is not connected", ++ MAC2STR(addr)); ++ return -1; ++ } ++ ++ switch (mode) { ++ case BEACON_REPORT_MODE_PASSIVE: ++ if (!(sta->rrm_enabled_capa[0] & ++ WLAN_RRM_CAPS_BEACON_REPORT_PASSIVE)) { ++ wpa_printf(MSG_INFO, ++ "Beacon request: " MACSTR ++ " does not support passive beacon report", ++ MAC2STR(addr)); ++ return -1; ++ } ++ break; ++ case BEACON_REPORT_MODE_ACTIVE: ++ if (!(sta->rrm_enabled_capa[0] & ++ WLAN_RRM_CAPS_BEACON_REPORT_ACTIVE)) { ++ wpa_printf(MSG_INFO, ++ "Beacon request: " MACSTR ++ " does not support active beacon report", ++ MAC2STR(addr)); ++ return -1; ++ } ++ break; ++ case BEACON_REPORT_MODE_TABLE: ++ if (!(sta->rrm_enabled_capa[0] & ++ WLAN_RRM_CAPS_BEACON_REPORT_TABLE)) { ++ wpa_printf(MSG_INFO, ++ "Beacon request: " MACSTR ++ " does not support table beacon report", ++ MAC2STR(addr)); ++ return -1; ++ } ++ break; ++ default: ++ wpa_printf(MSG_INFO, ++ "Beacon request: Unknown measurement mode %d", mode); ++ return -1; ++ } ++ ++ buf = wpabuf_alloc(5 + 2 + 3 + wpabuf_len(req)); ++ if (!buf) ++ return -1; ++ ++ hapd->beacon_req_token++; ++ if (!hapd->beacon_req_token) ++ hapd->beacon_req_token++; ++ ++ wpabuf_put_u8(buf, WLAN_ACTION_RADIO_MEASUREMENT); ++ wpabuf_put_u8(buf, WLAN_RRM_RADIO_MEASUREMENT_REQUEST); ++ wpabuf_put_u8(buf, hapd->beacon_req_token); ++ wpabuf_put_le16(buf, 0); /* Number of repetitions */ ++ ++ /* Measurement Request element */ ++ wpabuf_put_u8(buf, WLAN_EID_MEASURE_REQUEST); ++ wpabuf_put_u8(buf, 3 + wpabuf_len(req)); ++ wpabuf_put_u8(buf, 1); /* Measurement Token */ ++ wpabuf_put_u8(buf, req_mode); /* Measurement Request Mode */ ++ wpabuf_put_u8(buf, MEASURE_TYPE_BEACON); /* Measurement Type */ ++ wpabuf_put_buf(buf, req); ++ ++ ret = hostapd_drv_send_action(hapd, hapd->iface->freq, 0, addr, ++ wpabuf_head(buf), wpabuf_len(buf)); ++ wpabuf_free(buf); ++ if (ret < 0) ++ return ret; ++ ++ return hapd->beacon_req_token; ++} ++ ++ ++void hostapd_rrm_beacon_req_tx_status(struct hostapd_data *hapd, ++ const struct ieee80211_mgmt *mgmt, ++ size_t len, int ok) ++{ ++ if (len < 24 + 3) ++ return; ++ wpa_msg(hapd->msg_ctx, MSG_INFO, BEACON_REQ_TX_STATUS MACSTR ++ " %u ack=%d", MAC2STR(mgmt->da), ++ mgmt->u.action.u.rrm.dialog_token, ok); ++} +--- contrib/wpa/src/ap/rrm.h.orig ++++ contrib/wpa/src/ap/rrm.h +@@ -0,0 +1,33 @@ ++/* ++ * hostapd / Radio Measurement (RRM) ++ * Copyright(c) 2013 - 2016 Intel Mobile Communications GmbH. ++ * Copyright(c) 2011 - 2016 Intel Corporation. All rights reserved. ++ * ++ * This software may be distributed under the terms of the BSD license. ++ * See README for more details. ++ */ ++ ++#ifndef RRM_H ++#define RRM_H ++ ++/* ++ * Max measure request length is 255, -6 of the body we have 249 for the ++ * neighbor report elements. Each neighbor report element is at least 2 + 13 ++ * bytes, so we can't have more than 16 responders in the request. ++ */ ++#define RRM_RANGE_REQ_MAX_RESPONDERS 16 ++ ++void hostapd_handle_radio_measurement(struct hostapd_data *hapd, ++ const u8 *buf, size_t len); ++int hostapd_send_lci_req(struct hostapd_data *hapd, const u8 *addr); ++int hostapd_send_range_req(struct hostapd_data *hapd, const u8 *addr, ++ u16 random_interval, u8 min_ap, ++ const u8 *responders, unsigned int n_responders); ++void hostapd_clean_rrm(struct hostapd_data *hapd); ++int hostapd_send_beacon_req(struct hostapd_data *hapd, const u8 *addr, ++ u8 req_mode, const struct wpabuf *req); ++void hostapd_rrm_beacon_req_tx_status(struct hostapd_data *hapd, ++ const struct ieee80211_mgmt *mgmt, ++ size_t len, int ok); ++ ++#endif /* RRM_H */ +--- contrib/wpa/src/ap/sta_info.c.orig ++++ contrib/wpa/src/ap/sta_info.c +@@ -1,6 +1,6 @@ + /* + * hostapd / Station table +- * Copyright (c) 2002-2013, Jouni Malinen ++ * Copyright (c) 2002-2017, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. +@@ -13,10 +13,12 @@ + #include "common/ieee802_11_defs.h" + #include "common/wpa_ctrl.h" + #include "common/sae.h" ++#include "common/dpp.h" + #include "radius/radius.h" + #include "radius/radius_client.h" + #include "p2p/p2p.h" + #include "fst/fst.h" ++#include "crypto/crypto.h" + #include "hostapd.h" + #include "accounting.h" + #include "ieee802_1x.h" +@@ -32,8 +34,11 @@ + #include "ap_drv_ops.h" + #include "gas_serv.h" + #include "wnm_ap.h" ++#include "mbo_ap.h" + #include "ndisc_snoop.h" + #include "sta_info.h" ++#include "vlan.h" ++#include "wps_hostapd.h" + + static void ap_sta_remove_in_other_bss(struct hostapd_data *hapd, + struct sta_info *sta); +@@ -45,6 +50,7 @@ + static void ap_sa_query_timer(void *eloop_ctx, void *timeout_ctx); + #endif /* CONFIG_IEEE80211W */ + static int ap_sta_remove(struct hostapd_data *hapd, struct sta_info *sta); ++static void ap_sta_delayed_1x_auth_fail_cb(void *eloop_ctx, void *timeout_ctx); + + int ap_for_each_sta(struct hostapd_data *hapd, + int (*cb)(struct hostapd_data *hapd, struct sta_info *sta, +@@ -161,7 +167,7 @@ + /* just in case */ + ap_sta_set_authorized(hapd, sta, 0); + +- if (sta->flags & WLAN_STA_WDS) ++ if (sta->flags & (WLAN_STA_WDS | WLAN_STA_MULTI_AP)) + hostapd_set_wds_sta(hapd, NULL, sta->addr, sta->aid, 0); + + if (sta->ipaddr) +@@ -169,21 +175,10 @@ + ap_sta_ip6addr_del(hapd, sta); + + if (!hapd->iface->driver_ap_teardown && +- !(sta->flags & WLAN_STA_PREAUTH)) ++ !(sta->flags & WLAN_STA_PREAUTH)) { + hostapd_drv_sta_remove(hapd, sta->addr); +- +-#ifndef CONFIG_NO_VLAN +- if (sta->vlan_id_bound) { +- /* +- * Need to remove the STA entry before potentially removing the +- * VLAN. +- */ +- if (hapd->iface->driver_ap_teardown && +- !(sta->flags & WLAN_STA_PREAUTH)) +- hostapd_drv_sta_remove(hapd, sta->addr); +- vlan_remove_dynamic(hapd, sta->vlan_id_bound); ++ sta->added_unassoc = 0; + } +-#endif /* CONFIG_NO_VLAN */ + + ap_sta_hash_del(hapd, sta); + ap_sta_list_del(hapd, sta); +@@ -203,7 +198,8 @@ + if (sta->no_short_slot_time_set) { + sta->no_short_slot_time_set = 0; + hapd->iface->num_sta_no_short_slot_time--; +- if (hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G ++ if (hapd->iface->current_mode && ++ hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G + && hapd->iface->num_sta_no_short_slot_time == 0) + set_beacon++; + } +@@ -211,7 +207,8 @@ + if (sta->no_short_preamble_set) { + sta->no_short_preamble_set = 0; + hapd->iface->num_sta_no_short_preamble--; +- if (hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G ++ if (hapd->iface->current_mode && ++ hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G + && hapd->iface->num_sta_no_short_preamble == 0) + set_beacon++; + } +@@ -231,6 +228,13 @@ + hapd->iface->num_sta_ht_20mhz--; + } + ++#ifdef CONFIG_TAXONOMY ++ wpabuf_free(sta->probe_ie_taxonomy); ++ sta->probe_ie_taxonomy = NULL; ++ wpabuf_free(sta->assoc_ie_taxonomy); ++ sta->assoc_ie_taxonomy = NULL; ++#endif /* CONFIG_TAXONOMY */ ++ + #ifdef CONFIG_IEEE80211N + ht40_intolerant_remove(hapd->iface, sta); + #endif /* CONFIG_IEEE80211N */ +@@ -251,7 +255,7 @@ + + #ifdef CONFIG_MESH + if (hapd->mesh_sta_free_cb) +- hapd->mesh_sta_free_cb(sta); ++ hapd->mesh_sta_free_cb(hapd, sta); + #endif /* CONFIG_MESH */ + + if (set_beacon) +@@ -262,11 +266,10 @@ + eloop_cancel_timeout(ap_handle_timer, hapd, sta); + eloop_cancel_timeout(ap_handle_session_timer, hapd, sta); + eloop_cancel_timeout(ap_handle_session_warning_timer, hapd, sta); +- eloop_cancel_timeout(ap_sta_deauth_cb_timeout, hapd, sta); +- eloop_cancel_timeout(ap_sta_disassoc_cb_timeout, hapd, sta); ++ ap_sta_clear_disconnect_timeouts(hapd, sta); + sae_clear_retransmit_timer(hapd, sta); + +- ieee802_1x_free_station(sta); ++ ieee802_1x_free_station(hapd, sta); + wpa_auth_sta_deinit(sta->wpa_sm); + rsn_preauth_free_station(hapd, sta); + #ifndef CONFIG_NO_RADIUS +@@ -274,6 +277,28 @@ + radius_client_flush_auth(hapd->radius, sta->addr); + #endif /* CONFIG_NO_RADIUS */ + ++#ifndef CONFIG_NO_VLAN ++ /* ++ * sta->wpa_sm->group needs to be released before so that ++ * vlan_remove_dynamic() can check that no stations are left on the ++ * AP_VLAN netdev. ++ */ ++ if (sta->vlan_id) ++ vlan_remove_dynamic(hapd, sta->vlan_id); ++ if (sta->vlan_id_bound) { ++ /* ++ * Need to remove the STA entry before potentially removing the ++ * VLAN. ++ */ ++ if (hapd->iface->driver_ap_teardown && ++ !(sta->flags & WLAN_STA_PREAUTH)) { ++ hostapd_drv_sta_remove(hapd, sta->addr); ++ sta->added_unassoc = 0; ++ } ++ vlan_remove_dynamic(hapd, sta->vlan_id_bound); ++ } ++#endif /* CONFIG_NO_VLAN */ ++ + os_free(sta->challenge); + + #ifdef CONFIG_IEEE80211W +@@ -297,6 +322,7 @@ + wpabuf_free(sta->wps_ie); + wpabuf_free(sta->p2p_ie); + wpabuf_free(sta->hs20_ie); ++ wpabuf_free(sta->roaming_consortium); + #ifdef CONFIG_FST + wpabuf_free(sta->mb_ies); + #endif /* CONFIG_FST */ +@@ -303,10 +329,12 @@ + + os_free(sta->ht_capabilities); + os_free(sta->vht_capabilities); ++ os_free(sta->vht_operation); + hostapd_free_psk_list(sta->psk); + os_free(sta->identity); + os_free(sta->radius_cui); + os_free(sta->remediation_url); ++ os_free(sta->t_c_url); + wpabuf_free(sta->hs20_deauth_req); + os_free(sta->hs20_session_info_url); + +@@ -315,6 +343,39 @@ + os_free(sta->sae); + #endif /* CONFIG_SAE */ + ++ mbo_ap_sta_free(sta); ++ os_free(sta->supp_op_classes); ++ ++#ifdef CONFIG_FILS ++ os_free(sta->fils_pending_assoc_req); ++ wpabuf_free(sta->fils_hlp_resp); ++ wpabuf_free(sta->hlp_dhcp_discover); ++ eloop_cancel_timeout(fils_hlp_timeout, hapd, sta); ++#ifdef CONFIG_FILS_SK_PFS ++ crypto_ecdh_deinit(sta->fils_ecdh); ++ wpabuf_clear_free(sta->fils_dh_ss); ++ wpabuf_free(sta->fils_g_sta); ++#endif /* CONFIG_FILS_SK_PFS */ ++#endif /* CONFIG_FILS */ ++ ++#ifdef CONFIG_OWE ++ bin_clear_free(sta->owe_pmk, sta->owe_pmk_len); ++ crypto_ecdh_deinit(sta->owe_ecdh); ++#endif /* CONFIG_OWE */ ++ ++#ifdef CONFIG_DPP2 ++ dpp_pfs_free(sta->dpp_pfs); ++ sta->dpp_pfs = NULL; ++#endif /* CONFIG_DPP2 */ ++ ++ os_free(sta->ext_capability); ++ ++#ifdef CONFIG_WNM_AP ++ eloop_cancel_timeout(ap_sta_reset_steer_flag_timer, hapd, sta); ++#endif /* CONFIG_WNM_AP */ ++ ++ os_free(sta->ifname_wds); ++ + os_free(sta); + } + +@@ -354,8 +415,8 @@ + unsigned long next_time = 0; + int reason; + +- wpa_printf(MSG_DEBUG, "%s: " MACSTR " flags=0x%x timeout_next=%d", +- __func__, MAC2STR(sta->addr), sta->flags, ++ wpa_printf(MSG_DEBUG, "%s: %s: " MACSTR " flags=0x%x timeout_next=%d", ++ hapd->conf->iface, __func__, MAC2STR(sta->addr), sta->flags, + sta->timeout_next); + if (sta->timeout_next == STA_REMOVE) { + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, +@@ -446,6 +507,13 @@ + } else if (sta->timeout_next != STA_REMOVE) { + int deauth = sta->timeout_next == STA_DEAUTH; + ++ if (!deauth && !(sta->flags & WLAN_STA_ASSOC)) { ++ /* Cannot disassociate not-associated STA, so move ++ * directly to deauthentication. */ ++ sta->timeout_next = STA_DEAUTH; ++ deauth = 1; ++ } ++ + wpa_dbg(hapd->msg_ctx, MSG_DEBUG, + "Timeout, sending %s info to STA " MACSTR, + deauth ? "deauthentication" : "disassociation", +@@ -482,7 +550,7 @@ + sta->acct_terminate_cause = + RADIUS_ACCT_TERMINATE_CAUSE_IDLE_TIMEOUT; + accounting_sta_stop(hapd, sta); +- ieee802_1x_free_station(sta); ++ ieee802_1x_free_station(hapd, sta); + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_INFO, "disassociated due to " + "inactivity"); +@@ -519,6 +587,8 @@ + struct hostapd_data *hapd = eloop_ctx; + struct sta_info *sta = timeout_ctx; + ++ wpa_printf(MSG_DEBUG, "%s: Session timer for STA " MACSTR, ++ hapd->conf->iface, MAC2STR(sta->addr)); + if (!(sta->flags & WLAN_STA_AUTH)) { + if (sta->flags & WLAN_STA_GAS) { + wpa_printf(MSG_DEBUG, "GAS: Remove temporary STA " +@@ -573,18 +643,18 @@ + + static void ap_handle_session_warning_timer(void *eloop_ctx, void *timeout_ctx) + { +-#ifdef CONFIG_WNM ++#ifdef CONFIG_WNM_AP + struct hostapd_data *hapd = eloop_ctx; + struct sta_info *sta = timeout_ctx; + +- wpa_printf(MSG_DEBUG, "WNM: Session warning time reached for " MACSTR, +- MAC2STR(sta->addr)); ++ wpa_printf(MSG_DEBUG, "%s: WNM: Session warning time reached for " ++ MACSTR, hapd->conf->iface, MAC2STR(sta->addr)); + if (sta->hs20_session_info_url == NULL) + return; + + wnm_send_ess_disassoc_imminent(hapd, sta, sta->hs20_session_info_url, + sta->hs20_disassoc_timer); +-#endif /* CONFIG_WNM */ ++#endif /* CONFIG_WNM_AP */ + } + + +@@ -619,7 +689,10 @@ + return NULL; + } + sta->acct_interim_interval = hapd->conf->acct_interim_interval; +- accounting_sta_get_id(hapd, sta); ++ if (accounting_sta_get_id(hapd, sta) < 0) { ++ os_free(sta); ++ return NULL; ++ } + + if (!(hapd->iface->drv_flags & WPA_DRIVER_FLAGS_INACTIVITY_TIMER)) { + wpa_printf(MSG_DEBUG, "%s: register ap_handle_timer timeout " +@@ -640,6 +713,11 @@ + sta->last_seq_ctrl = WLAN_INVALID_MGMT_SEQ; + dl_list_init(&sta->ip6addr); + ++#ifdef CONFIG_TAXONOMY ++ sta_track_claim_taxonomy_info(hapd->iface, addr, ++ &sta->probe_ie_taxonomy); ++#endif /* CONFIG_TAXONOMY */ ++ + return sta; + } + +@@ -652,14 +730,16 @@ + hostapd_drv_br_delete_ip_neigh(hapd, 4, (u8 *) &sta->ipaddr); + ap_sta_ip6addr_del(hapd, sta); + +- wpa_printf(MSG_DEBUG, "Removing STA " MACSTR " from kernel driver", +- MAC2STR(sta->addr)); ++ wpa_printf(MSG_DEBUG, "%s: Removing STA " MACSTR " from kernel driver", ++ hapd->conf->iface, MAC2STR(sta->addr)); + if (hostapd_drv_sta_remove(hapd, sta->addr) && + sta->flags & WLAN_STA_ASSOC) { +- wpa_printf(MSG_DEBUG, "Could not remove station " MACSTR +- " from kernel driver.", MAC2STR(sta->addr)); ++ wpa_printf(MSG_DEBUG, "%s: Could not remove station " MACSTR ++ " from kernel driver", ++ hapd->conf->iface, MAC2STR(sta->addr)); + return -1; + } ++ sta->added_unassoc = 0; + return 0; + } + +@@ -683,6 +763,10 @@ + if (!sta2) + continue; + ++ wpa_printf(MSG_DEBUG, "%s: disconnect old STA " MACSTR ++ " association from another BSS %s", ++ hapd->conf->iface, MAC2STR(sta2->addr), ++ bss->conf->iface); + ap_sta_disconnect(bss, sta2, sta2->addr, + WLAN_REASON_PREV_AUTH_NOT_VALID); + } +@@ -694,6 +778,8 @@ + struct hostapd_data *hapd = eloop_ctx; + struct sta_info *sta = timeout_ctx; + ++ wpa_printf(MSG_DEBUG, "%s: Disassociation callback for STA " MACSTR, ++ hapd->conf->iface, MAC2STR(sta->addr)); + ap_sta_remove(hapd, sta); + mlme_disassociate_indication(hapd, sta, sta->disassoc_reason); + } +@@ -705,9 +791,17 @@ + wpa_printf(MSG_DEBUG, "%s: disassociate STA " MACSTR, + hapd->conf->iface, MAC2STR(sta->addr)); + sta->last_seq_ctrl = WLAN_INVALID_MGMT_SEQ; +- sta->flags &= ~(WLAN_STA_ASSOC | WLAN_STA_ASSOC_REQ_OK); ++ if (hapd->iface->current_mode && ++ hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211AD) { ++ /* Skip deauthentication in DMG/IEEE 802.11ad */ ++ sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC | ++ WLAN_STA_ASSOC_REQ_OK); ++ sta->timeout_next = STA_REMOVE; ++ } else { ++ sta->flags &= ~(WLAN_STA_ASSOC | WLAN_STA_ASSOC_REQ_OK); ++ sta->timeout_next = STA_DEAUTH; ++ } + ap_sta_set_authorized(hapd, sta, 0); +- sta->timeout_next = STA_DEAUTH; + wpa_printf(MSG_DEBUG, "%s: reschedule ap_handle_timer timeout " + "for " MACSTR " (%d seconds - " + "AP_MAX_INACTIVITY_AFTER_DISASSOC)", +@@ -717,7 +811,9 @@ + eloop_register_timeout(AP_MAX_INACTIVITY_AFTER_DISASSOC, 0, + ap_handle_timer, hapd, sta); + accounting_sta_stop(hapd, sta); +- ieee802_1x_free_station(sta); ++ ieee802_1x_free_station(hapd, sta); ++ wpa_auth_sta_deinit(sta->wpa_sm); ++ sta->wpa_sm = NULL; + + sta->disassoc_reason = reason; + sta->flags |= WLAN_STA_PENDING_DISASSOC_CB; +@@ -733,6 +829,8 @@ + struct hostapd_data *hapd = eloop_ctx; + struct sta_info *sta = timeout_ctx; + ++ wpa_printf(MSG_DEBUG, "%s: Deauthentication callback for STA " MACSTR, ++ hapd->conf->iface, MAC2STR(sta->addr)); + ap_sta_remove(hapd, sta); + mlme_deauthenticate_indication(hapd, sta, sta->deauth_reason); + } +@@ -741,6 +839,14 @@ + void ap_sta_deauthenticate(struct hostapd_data *hapd, struct sta_info *sta, + u16 reason) + { ++ if (hapd->iface->current_mode && ++ hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211AD) { ++ /* Deauthentication is not used in DMG/IEEE 802.11ad; ++ * disassociate the STA instead. */ ++ ap_sta_disassociate(hapd, sta, reason); ++ return; ++ } ++ + wpa_printf(MSG_DEBUG, "%s: deauthenticate STA " MACSTR, + hapd->conf->iface, MAC2STR(sta->addr)); + sta->last_seq_ctrl = WLAN_INVALID_MGMT_SEQ; +@@ -756,7 +862,7 @@ + eloop_register_timeout(AP_MAX_INACTIVITY_AFTER_DEAUTH, 0, + ap_handle_timer, hapd, sta); + accounting_sta_stop(hapd, sta); +- ieee802_1x_free_station(sta); ++ ieee802_1x_free_station(hapd, sta); + + sta->deauth_reason = reason; + sta->flags |= WLAN_STA_PENDING_DEAUTH_CB; +@@ -784,6 +890,125 @@ + #endif /* CONFIG_WPS */ + + ++static int ap_sta_get_free_vlan_id(struct hostapd_data *hapd) ++{ ++ struct hostapd_vlan *vlan; ++ int vlan_id = MAX_VLAN_ID + 2; ++ ++retry: ++ for (vlan = hapd->conf->vlan; vlan; vlan = vlan->next) { ++ if (vlan->vlan_id == vlan_id) { ++ vlan_id++; ++ goto retry; ++ } ++ } ++ return vlan_id; ++} ++ ++ ++int ap_sta_set_vlan(struct hostapd_data *hapd, struct sta_info *sta, ++ struct vlan_description *vlan_desc) ++{ ++ struct hostapd_vlan *vlan = NULL, *wildcard_vlan = NULL; ++ int old_vlan_id, vlan_id = 0, ret = 0; ++ ++ /* Check if there is something to do */ ++ if (hapd->conf->ssid.per_sta_vif && !sta->vlan_id) { ++ /* This sta is lacking its own vif */ ++ } else if (hapd->conf->ssid.dynamic_vlan == DYNAMIC_VLAN_DISABLED && ++ !hapd->conf->ssid.per_sta_vif && sta->vlan_id) { ++ /* sta->vlan_id needs to be reset */ ++ } else if (!vlan_compare(vlan_desc, sta->vlan_desc)) { ++ return 0; /* nothing to change */ ++ } ++ ++ /* Now the real VLAN changed or the STA just needs its own vif */ ++ if (hapd->conf->ssid.per_sta_vif) { ++ /* Assign a new vif, always */ ++ /* find a free vlan_id sufficiently big */ ++ vlan_id = ap_sta_get_free_vlan_id(hapd); ++ /* Get wildcard VLAN */ ++ for (vlan = hapd->conf->vlan; vlan; vlan = vlan->next) { ++ if (vlan->vlan_id == VLAN_ID_WILDCARD) ++ break; ++ } ++ if (!vlan) { ++ hostapd_logger(hapd, sta->addr, ++ HOSTAPD_MODULE_IEEE80211, ++ HOSTAPD_LEVEL_DEBUG, ++ "per_sta_vif missing wildcard"); ++ vlan_id = 0; ++ ret = -1; ++ goto done; ++ } ++ } else if (vlan_desc && vlan_desc->notempty) { ++ for (vlan = hapd->conf->vlan; vlan; vlan = vlan->next) { ++ if (!vlan_compare(&vlan->vlan_desc, vlan_desc)) ++ break; ++ if (vlan->vlan_id == VLAN_ID_WILDCARD) ++ wildcard_vlan = vlan; ++ } ++ if (vlan) { ++ vlan_id = vlan->vlan_id; ++ } else if (wildcard_vlan) { ++ vlan = wildcard_vlan; ++ vlan_id = vlan_desc->untagged; ++ if (vlan_desc->tagged[0]) { ++ /* Tagged VLAN configuration */ ++ vlan_id = ap_sta_get_free_vlan_id(hapd); ++ } ++ } else { ++ hostapd_logger(hapd, sta->addr, ++ HOSTAPD_MODULE_IEEE80211, ++ HOSTAPD_LEVEL_DEBUG, ++ "missing vlan and wildcard for vlan=%d%s", ++ vlan_desc->untagged, ++ vlan_desc->tagged[0] ? "+" : ""); ++ vlan_id = 0; ++ ret = -1; ++ goto done; ++ } ++ } ++ ++ if (vlan && vlan->vlan_id == VLAN_ID_WILDCARD) { ++ vlan = vlan_add_dynamic(hapd, vlan, vlan_id, vlan_desc); ++ if (vlan == NULL) { ++ hostapd_logger(hapd, sta->addr, ++ HOSTAPD_MODULE_IEEE80211, ++ HOSTAPD_LEVEL_DEBUG, ++ "could not add dynamic VLAN interface for vlan=%d%s", ++ vlan_desc ? vlan_desc->untagged : -1, ++ (vlan_desc && vlan_desc->tagged[0]) ? ++ "+" : ""); ++ vlan_id = 0; ++ ret = -1; ++ goto done; ++ } ++ ++ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, ++ HOSTAPD_LEVEL_DEBUG, ++ "added new dynamic VLAN interface '%s'", ++ vlan->ifname); ++ } else if (vlan && vlan->dynamic_vlan > 0) { ++ vlan->dynamic_vlan++; ++ hostapd_logger(hapd, sta->addr, ++ HOSTAPD_MODULE_IEEE80211, ++ HOSTAPD_LEVEL_DEBUG, ++ "updated existing dynamic VLAN interface '%s'", ++ vlan->ifname); ++ } ++done: ++ old_vlan_id = sta->vlan_id; ++ sta->vlan_id = vlan_id; ++ sta->vlan_desc = vlan ? &vlan->vlan_desc : NULL; ++ ++ if (vlan_id != old_vlan_id && old_vlan_id) ++ vlan_remove_dynamic(hapd, old_vlan_id); ++ ++ return ret; ++} ++ ++ + int ap_sta_bind_vlan(struct hostapd_data *hapd, struct sta_info *sta) + { + #ifndef CONFIG_NO_VLAN +@@ -796,20 +1021,11 @@ + if (hapd->conf->ssid.vlan[0]) + iface = hapd->conf->ssid.vlan; + +- if (hapd->conf->ssid.dynamic_vlan == DYNAMIC_VLAN_DISABLED) +- sta->vlan_id = 0; +- else if (sta->vlan_id > 0) { +- struct hostapd_vlan *wildcard_vlan = NULL; +- vlan = hapd->conf->vlan; +- while (vlan) { ++ if (sta->vlan_id > 0) { ++ for (vlan = hapd->conf->vlan; vlan; vlan = vlan->next) { + if (vlan->vlan_id == sta->vlan_id) + break; +- if (vlan->vlan_id == VLAN_ID_WILDCARD) +- wildcard_vlan = vlan; +- vlan = vlan->next; + } +- if (!vlan) +- vlan = wildcard_vlan; + if (vlan) + iface = vlan->ifname; + } +@@ -829,54 +1045,13 @@ + sta->vlan_id); + ret = -1; + goto done; +- } else if (sta->vlan_id > 0 && vlan->vlan_id == VLAN_ID_WILDCARD) { +- vlan = vlan_add_dynamic(hapd, vlan, sta->vlan_id); +- if (vlan == NULL) { +- hostapd_logger(hapd, sta->addr, +- HOSTAPD_MODULE_IEEE80211, +- HOSTAPD_LEVEL_DEBUG, "could not add " +- "dynamic VLAN interface for vlan_id=%d", +- sta->vlan_id); +- ret = -1; +- goto done; +- } +- +- iface = vlan->ifname; +- if (vlan_setup_encryption_dyn(hapd, iface) != 0) { +- hostapd_logger(hapd, sta->addr, +- HOSTAPD_MODULE_IEEE80211, +- HOSTAPD_LEVEL_DEBUG, "could not " +- "configure encryption for dynamic VLAN " +- "interface for vlan_id=%d", +- sta->vlan_id); +- } +- +- hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, +- HOSTAPD_LEVEL_DEBUG, "added new dynamic VLAN " +- "interface '%s'", iface); +- } else if (vlan && vlan->vlan_id == sta->vlan_id) { +- if (vlan->dynamic_vlan > 0) { +- vlan->dynamic_vlan++; +- hostapd_logger(hapd, sta->addr, +- HOSTAPD_MODULE_IEEE80211, +- HOSTAPD_LEVEL_DEBUG, "updated existing " +- "dynamic VLAN interface '%s'", iface); +- } +- +- /* +- * Update encryption configuration for statically generated +- * VLAN interface. This is only used for static WEP +- * configuration for the case where hostapd did not yet know +- * which keys are to be used when the interface was added. +- */ +- if (vlan_setup_encryption_dyn(hapd, iface) != 0) { +- hostapd_logger(hapd, sta->addr, +- HOSTAPD_MODULE_IEEE80211, +- HOSTAPD_LEVEL_DEBUG, "could not " +- "configure encryption for VLAN " +- "interface for vlan_id=%d", +- sta->vlan_id); +- } ++ } else if (vlan && vlan->dynamic_vlan > 0) { ++ vlan->dynamic_vlan++; ++ hostapd_logger(hapd, sta->addr, ++ HOSTAPD_MODULE_IEEE80211, ++ HOSTAPD_LEVEL_DEBUG, ++ "updated existing dynamic VLAN interface '%s'", ++ iface); + } + + /* ref counters have been increased, so mark the station */ +@@ -942,6 +1117,10 @@ + unsigned int timeout, sec, usec; + u8 *trans_id, *nbuf; + ++ wpa_printf(MSG_DEBUG, "%s: SA Query timer for STA " MACSTR ++ " (count=%d)", ++ hapd->conf->iface, MAC2STR(sta->addr), sta->sa_query_count); ++ + if (sta->sa_query_count > 0 && + ap_check_sa_query_timeout(hapd, sta)) + return; +@@ -999,6 +1178,32 @@ + #endif /* CONFIG_IEEE80211W */ + + ++const char * ap_sta_wpa_get_keyid(struct hostapd_data *hapd, ++ struct sta_info *sta) ++{ ++ struct hostapd_wpa_psk *psk; ++ struct hostapd_ssid *ssid; ++ const u8 *pmk; ++ int pmk_len; ++ ++ ssid = &hapd->conf->ssid; ++ ++ pmk = wpa_auth_get_pmk(sta->wpa_sm, &pmk_len); ++ if (!pmk || pmk_len != PMK_LEN) ++ return NULL; ++ ++ for (psk = ssid->wpa_psk; psk; psk = psk->next) ++ if (os_memcmp(pmk, psk->psk, PMK_LEN) == 0) ++ break; ++ if (!psk) ++ return NULL; ++ if (!psk || !psk->keyid[0]) ++ return NULL; ++ ++ return psk->keyid; ++} ++ ++ + void ap_sta_set_authorized(struct hostapd_data *hapd, struct sta_info *sta, + int authorized) + { +@@ -1037,7 +1242,11 @@ + sta->addr, authorized, dev_addr); + + if (authorized) { ++ const char *keyid; ++ char keyid_buf[100]; + char ip_addr[100]; ++ ++ keyid_buf[0] = '\0'; + ip_addr[0] = '\0'; + #ifdef CONFIG_P2P + if (wpa_auth_get_ip_addr(sta->wpa_sm, ip_addr_buf) == 0) { +@@ -1048,14 +1257,20 @@ + } + #endif /* CONFIG_P2P */ + +- wpa_msg(hapd->msg_ctx, MSG_INFO, AP_STA_CONNECTED "%s%s", +- buf, ip_addr); ++ keyid = ap_sta_wpa_get_keyid(hapd, sta); ++ if (keyid) { ++ os_snprintf(keyid_buf, sizeof(keyid_buf), ++ " keyid=%s", keyid); ++ } + ++ wpa_msg(hapd->msg_ctx, MSG_INFO, AP_STA_CONNECTED "%s%s%s", ++ buf, ip_addr, keyid_buf); ++ + if (hapd->msg_ctx_parent && + hapd->msg_ctx_parent != hapd->msg_ctx) + wpa_msg_no_global(hapd->msg_ctx_parent, MSG_INFO, +- AP_STA_CONNECTED "%s%s", +- buf, ip_addr); ++ AP_STA_CONNECTED "%s%s%s", ++ buf, ip_addr, keyid_buf); + } else { + wpa_msg(hapd->msg_ctx, MSG_INFO, AP_STA_DISCONNECTED "%s", buf); + +@@ -1080,6 +1295,14 @@ + void ap_sta_disconnect(struct hostapd_data *hapd, struct sta_info *sta, + const u8 *addr, u16 reason) + { ++ if (sta) ++ wpa_printf(MSG_DEBUG, "%s: %s STA " MACSTR " reason=%u", ++ hapd->conf->iface, __func__, MAC2STR(sta->addr), ++ reason); ++ else if (addr) ++ wpa_printf(MSG_DEBUG, "%s: %s addr " MACSTR " reason=%u", ++ hapd->conf->iface, __func__, MAC2STR(addr), ++ reason); + + if (sta == NULL && addr) + sta = ap_get_sta(hapd, addr); +@@ -1093,10 +1316,10 @@ + wpa_auth_sm_event(sta->wpa_sm, WPA_DEAUTH); + ieee802_1x_notify_port_enabled(sta->eapol_sm, 0); + sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC); +- wpa_printf(MSG_DEBUG, "%s: reschedule ap_handle_timer timeout " ++ wpa_printf(MSG_DEBUG, "%s: %s: reschedule ap_handle_timer timeout " + "for " MACSTR " (%d seconds - " + "AP_MAX_INACTIVITY_AFTER_DEAUTH)", +- __func__, MAC2STR(sta->addr), ++ hapd->conf->iface, __func__, MAC2STR(sta->addr), + AP_MAX_INACTIVITY_AFTER_DEAUTH); + eloop_cancel_timeout(ap_handle_timer, hapd, sta); + eloop_register_timeout(AP_MAX_INACTIVITY_AFTER_DEAUTH, 0, +@@ -1103,6 +1326,20 @@ + ap_handle_timer, hapd, sta); + sta->timeout_next = STA_REMOVE; + ++ if (hapd->iface->current_mode && ++ hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211AD) { ++ /* Deauthentication is not used in DMG/IEEE 802.11ad; ++ * disassociate the STA instead. */ ++ sta->disassoc_reason = reason; ++ sta->flags |= WLAN_STA_PENDING_DISASSOC_CB; ++ eloop_cancel_timeout(ap_sta_disassoc_cb_timeout, hapd, sta); ++ eloop_register_timeout(hapd->iface->drv_flags & ++ WPA_DRIVER_FLAGS_DEAUTH_TX_STATUS ? ++ 2 : 0, 0, ap_sta_disassoc_cb_timeout, ++ hapd, sta); ++ return; ++ } ++ + sta->deauth_reason = reason; + sta->flags |= WLAN_STA_PENDING_DEAUTH_CB; + eloop_cancel_timeout(ap_sta_deauth_cb_timeout, hapd, sta); +@@ -1136,12 +1373,37 @@ + } + + ++void ap_sta_clear_disconnect_timeouts(struct hostapd_data *hapd, ++ struct sta_info *sta) ++{ ++ if (eloop_cancel_timeout(ap_sta_deauth_cb_timeout, hapd, sta) > 0) ++ wpa_printf(MSG_DEBUG, ++ "%s: Removed ap_sta_deauth_cb_timeout timeout for " ++ MACSTR, ++ hapd->conf->iface, MAC2STR(sta->addr)); ++ if (eloop_cancel_timeout(ap_sta_disassoc_cb_timeout, hapd, sta) > 0) ++ wpa_printf(MSG_DEBUG, ++ "%s: Removed ap_sta_disassoc_cb_timeout timeout for " ++ MACSTR, ++ hapd->conf->iface, MAC2STR(sta->addr)); ++ if (eloop_cancel_timeout(ap_sta_delayed_1x_auth_fail_cb, hapd, sta) > 0) ++ { ++ wpa_printf(MSG_DEBUG, ++ "%s: Removed ap_sta_delayed_1x_auth_fail_cb timeout for " ++ MACSTR, ++ hapd->conf->iface, MAC2STR(sta->addr)); ++ if (sta->flags & WLAN_STA_WPS) ++ hostapd_wps_eap_completed(hapd); ++ } ++} ++ ++ + int ap_sta_flags_txt(u32 flags, char *buf, size_t buflen) + { + int res; + + buf[0] = '\0'; +- res = os_snprintf(buf, buflen, "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s", ++ res = os_snprintf(buf, buflen, "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s", + (flags & WLAN_STA_AUTH ? "[AUTH]" : ""), + (flags & WLAN_STA_ASSOC ? "[ASSOC]" : ""), + (flags & WLAN_STA_AUTHORIZED ? "[AUTHORIZED]" : ""), +@@ -1158,6 +1420,7 @@ + (flags & WLAN_STA_NONERP ? "[NonERP]" : ""), + (flags & WLAN_STA_WPS2 ? "[WPS2]" : ""), + (flags & WLAN_STA_GAS ? "[GAS]" : ""), ++ (flags & WLAN_STA_HT ? "[HT]" : ""), + (flags & WLAN_STA_VHT ? "[VHT]" : ""), + (flags & WLAN_STA_VENDOR_VHT ? "[VENDOR_VHT]" : ""), + (flags & WLAN_STA_WNM_SLEEP_MODE ? +@@ -1167,3 +1430,48 @@ + + return res; + } ++ ++ ++static void ap_sta_delayed_1x_auth_fail_cb(void *eloop_ctx, void *timeout_ctx) ++{ ++ struct hostapd_data *hapd = eloop_ctx; ++ struct sta_info *sta = timeout_ctx; ++ u16 reason; ++ ++ wpa_dbg(hapd->msg_ctx, MSG_DEBUG, ++ "IEEE 802.1X: Scheduled disconnection of " MACSTR ++ " after EAP-Failure", MAC2STR(sta->addr)); ++ ++ reason = sta->disconnect_reason_code; ++ if (!reason) ++ reason = WLAN_REASON_IEEE_802_1X_AUTH_FAILED; ++ ap_sta_disconnect(hapd, sta, sta->addr, reason); ++ if (sta->flags & WLAN_STA_WPS) ++ hostapd_wps_eap_completed(hapd); ++} ++ ++ ++void ap_sta_delayed_1x_auth_fail_disconnect(struct hostapd_data *hapd, ++ struct sta_info *sta) ++{ ++ wpa_dbg(hapd->msg_ctx, MSG_DEBUG, ++ "IEEE 802.1X: Force disconnection of " MACSTR ++ " after EAP-Failure in 10 ms", MAC2STR(sta->addr)); ++ ++ /* ++ * Add a small sleep to increase likelihood of previously requested ++ * EAP-Failure TX getting out before this should the driver reorder ++ * operations. ++ */ ++ eloop_cancel_timeout(ap_sta_delayed_1x_auth_fail_cb, hapd, sta); ++ eloop_register_timeout(0, 10000, ap_sta_delayed_1x_auth_fail_cb, ++ hapd, sta); ++} ++ ++ ++int ap_sta_pending_delayed_1x_auth_fail_disconnect(struct hostapd_data *hapd, ++ struct sta_info *sta) ++{ ++ return eloop_is_timeout_registered(ap_sta_delayed_1x_auth_fail_cb, ++ hapd, sta); ++} +--- contrib/wpa/src/ap/sta_info.h.orig ++++ contrib/wpa/src/ap/sta_info.h +@@ -1,6 +1,6 @@ + /* + * hostapd / Station table +- * Copyright (c) 2002-2011, Jouni Malinen ++ * Copyright (c) 2002-2017, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. +@@ -9,12 +9,11 @@ + #ifndef STA_INFO_H + #define STA_INFO_H + +-#ifdef CONFIG_MESH +-/* needed for mesh_plink_state enum */ + #include "common/defs.h" +-#endif /* CONFIG_MESH */ +- + #include "list.h" ++#include "vlan.h" ++#include "common/wpa_common.h" ++#include "common/ieee802_11_defs.h" + + /* STA flags */ + #define WLAN_STA_AUTH BIT(0) +@@ -36,6 +35,8 @@ + #define WLAN_STA_WNM_SLEEP_MODE BIT(19) + #define WLAN_STA_VHT_OPMODE_ENABLED BIT(20) + #define WLAN_STA_VENDOR_VHT BIT(21) ++#define WLAN_STA_PENDING_FILS_ERP BIT(22) ++#define WLAN_STA_MULTI_AP BIT(23) + #define WLAN_STA_PENDING_DISASSOC_CB BIT(29) + #define WLAN_STA_PENDING_DEAUTH_CB BIT(30) + #define WLAN_STA_NONERP BIT(31) +@@ -44,7 +45,22 @@ + * Supported Rates IEs). */ + #define WLAN_SUPP_RATES_MAX 32 + ++struct hostapd_data; + ++struct mbo_non_pref_chan_info { ++ struct mbo_non_pref_chan_info *next; ++ u8 op_class; ++ u8 pref; ++ u8 reason_code; ++ u8 num_channels; ++ u8 channels[]; ++}; ++ ++struct pending_eapol_rx { ++ struct wpabuf *buf; ++ struct os_reltime rx_time; ++}; ++ + struct sta_info { + struct sta_info *next; /* next entry in sta list */ + struct sta_info *hnext; /* next entry in hash table list */ +@@ -52,6 +68,7 @@ + be32 ipaddr; + struct dl_list ip6addr; /* list head for struct ip6addr */ + u16 aid; /* STA's unique AID (1 .. 2007) or 0 if not yet assigned */ ++ u16 disconnect_reason_code; /* RADIUS server override */ + u32 flags; /* Bitfield of WLAN_STA_* */ + u16 capability; + u16 listen_interval; /* or beacon_int for APs */ +@@ -63,13 +80,22 @@ + enum mesh_plink_state plink_state; + u16 peer_lid; + u16 my_lid; ++ u16 peer_aid; + u16 mpm_close_reason; + int mpm_retries; +- u8 my_nonce[32]; +- u8 peer_nonce[32]; ++ u8 my_nonce[WPA_NONCE_LEN]; ++ u8 peer_nonce[WPA_NONCE_LEN]; + u8 aek[32]; /* SHA256 digest length */ +- u8 mtk[16]; +- u8 mgtk[16]; ++ u8 mtk[WPA_TK_MAX_LEN]; ++ size_t mtk_len; ++ u8 mgtk_rsc[6]; ++ u8 mgtk_key_id; ++ u8 mgtk[WPA_TK_MAX_LEN]; ++ size_t mgtk_len; ++ u8 igtk_rsc[6]; ++ u8 igtk[WPA_TK_MAX_LEN]; ++ size_t igtk_len; ++ u16 igtk_key_id; + u8 sae_auth_retry; + #endif /* CONFIG_MESH */ + +@@ -86,6 +112,13 @@ + unsigned int hs20_deauth_requested:1; + unsigned int session_timeout_set:1; + unsigned int radius_das_match:1; ++ unsigned int ecsa_supported:1; ++ unsigned int added_unassoc:1; ++ unsigned int pending_wds_enable:1; ++ unsigned int power_capab:1; ++ unsigned int agreed_to_steer:1; ++ unsigned int hs20_t_c_filtering:1; ++ unsigned int ft_over_ds:1; + + u16 auth_alg; + +@@ -100,17 +133,20 @@ + /* IEEE 802.1X related data */ + struct eapol_state_machine *eapol_sm; + +- u32 acct_session_id_hi; +- u32 acct_session_id_lo; ++ struct pending_eapol_rx *pending_eapol_rx; ++ ++ u64 acct_session_id; + struct os_reltime acct_session_start; + int acct_session_started; + int acct_terminate_cause; /* Acct-Terminate-Cause */ + int acct_interim_interval; /* Acct-Interim-Interval */ ++ unsigned int acct_interim_errors; + +- unsigned long last_rx_bytes; +- unsigned long last_tx_bytes; +- u32 acct_input_gigawords; /* Acct-Input-Gigawords */ +- u32 acct_output_gigawords; /* Acct-Output-Gigawords */ ++ /* For extending 32-bit driver counters to 64-bit counters */ ++ u32 last_rx_bytes_hi; ++ u32 last_rx_bytes_lo; ++ u32 last_tx_bytes_hi; ++ u32 last_tx_bytes_lo; + + u8 *challenge; /* IEEE 802.11 Shared Key Authentication Challenge */ + +@@ -118,6 +154,7 @@ + struct rsn_preauth_interface *preauth_iface; + + int vlan_id; /* 0: none, >0: VID */ ++ struct vlan_description *vlan_desc; + int vlan_id_bound; /* updated by ap_sta_bind_vlan() */ + /* PSKs from RADIUS authentication server */ + struct hostapd_sta_wpa_psk_short *psk; +@@ -127,6 +164,7 @@ + + struct ieee80211_ht_capabilities *ht_capabilities; + struct ieee80211_vht_capabilities *vht_capabilities; ++ struct ieee80211_vht_operation *vht_operation; + u8 vht_opmode; + + #ifdef CONFIG_IEEE80211W +@@ -139,17 +177,20 @@ + struct os_reltime sa_query_start; + #endif /* CONFIG_IEEE80211W */ + +-#ifdef CONFIG_INTERWORKING ++#if defined(CONFIG_INTERWORKING) || defined(CONFIG_DPP) + #define GAS_DIALOG_MAX 8 /* Max concurrent dialog number */ + struct gas_dialog_info *gas_dialog; + u8 gas_dialog_next; +-#endif /* CONFIG_INTERWORKING */ ++#endif /* CONFIG_INTERWORKING || CONFIG_DPP */ + + struct wpabuf *wps_ie; /* WPS IE from (Re)Association Request */ + struct wpabuf *p2p_ie; /* P2P IE from (Re)Association Request */ + struct wpabuf *hs20_ie; /* HS 2.0 IE from (Re)Association Request */ ++ /* Hotspot 2.0 Roaming Consortium from (Re)Association Request */ ++ struct wpabuf *roaming_consortium; + u8 remediation_method; + char *remediation_url; /* HS 2.0 Subscription Remediation Server URL */ ++ char *t_c_url; /* HS 2.0 Terms and Conditions Server URL */ + struct wpabuf *hs20_deauth_req; + char *hs20_session_info_url; + int hs20_disassoc_timer; +@@ -161,9 +202,11 @@ + + #ifdef CONFIG_SAE + struct sae_data *sae; ++ unsigned int mesh_sae_pmksa_caching:1; + #endif /* CONFIG_SAE */ + +- u32 session_timeout; /* valid only if session_timeout_set == 1 */ ++ /* valid only if session_timeout_set == 1 */ ++ struct os_reltime session_timeout; + + /* Last Authentication/(Re)Association Request/Action frame sequence + * control */ +@@ -170,6 +213,68 @@ + u16 last_seq_ctrl; + /* Last Authentication/(Re)Association Request/Action frame subtype */ + u8 last_subtype; ++ ++#ifdef CONFIG_MBO ++ u8 cell_capa; /* 0 = unknown (not an MBO STA); otherwise, ++ * enum mbo_cellular_capa values */ ++ struct mbo_non_pref_chan_info *non_pref_chan; ++ int auth_rssi; /* Last Authentication frame RSSI */ ++#endif /* CONFIG_MBO */ ++ ++ u8 *supp_op_classes; /* Supported Operating Classes element, if ++ * received, starting from the Length field */ ++ ++ u8 rrm_enabled_capa[5]; ++ ++ s8 min_tx_power; ++ s8 max_tx_power; ++ ++#ifdef CONFIG_TAXONOMY ++ struct wpabuf *probe_ie_taxonomy; ++ struct wpabuf *assoc_ie_taxonomy; ++#endif /* CONFIG_TAXONOMY */ ++ ++#ifdef CONFIG_FILS ++ u8 fils_snonce[FILS_NONCE_LEN]; ++ u8 fils_session[FILS_SESSION_LEN]; ++ u8 fils_erp_pmkid[PMKID_LEN]; ++ u8 *fils_pending_assoc_req; ++ size_t fils_pending_assoc_req_len; ++ unsigned int fils_pending_assoc_is_reassoc:1; ++ unsigned int fils_dhcp_rapid_commit_proxy:1; ++ unsigned int fils_erp_pmkid_set:1; ++ unsigned int fils_drv_assoc_finish:1; ++ struct wpabuf *fils_hlp_resp; ++ struct wpabuf *hlp_dhcp_discover; ++ void (*fils_pending_cb)(struct hostapd_data *hapd, struct sta_info *sta, ++ u16 resp, struct wpabuf *data, int pub); ++#ifdef CONFIG_FILS_SK_PFS ++ struct crypto_ecdh *fils_ecdh; ++#endif /* CONFIG_FILS_SK_PFS */ ++ struct wpabuf *fils_dh_ss; ++ struct wpabuf *fils_g_sta; ++#endif /* CONFIG_FILS */ ++ ++#ifdef CONFIG_OWE ++ u8 *owe_pmk; ++ size_t owe_pmk_len; ++ struct crypto_ecdh *owe_ecdh; ++ u16 owe_group; ++#endif /* CONFIG_OWE */ ++ ++ u8 *ext_capability; ++ char *ifname_wds; /* WDS ifname, if in use */ ++ ++#ifdef CONFIG_DPP2 ++ struct dpp_pfs *dpp_pfs; ++#endif /* CONFIG_DPP2 */ ++ ++#ifdef CONFIG_TESTING_OPTIONS ++ enum wpa_alg last_tk_alg; ++ int last_tk_key_idx; ++ u8 last_tk[WPA_TK_MAX_LEN]; ++ size_t last_tk_len; ++#endif /* CONFIG_TESTING_OPTIONS */ + }; + + +@@ -180,7 +285,7 @@ + * AP_DISASSOC_DELAY seconds. Similarly, the station will be deauthenticated + * after AP_DEAUTH_DELAY seconds has passed after disassociation. */ + #define AP_MAX_INACTIVITY (5 * 60) +-#define AP_DISASSOC_DELAY (1) ++#define AP_DISASSOC_DELAY (3) + #define AP_DEAUTH_DELAY (1) + /* Number of seconds to keep STA entry with Authenticated flag after it has + * been disassociated. */ +@@ -189,8 +294,6 @@ + #define AP_MAX_INACTIVITY_AFTER_DEAUTH (1 * 5) + + +-struct hostapd_data; +- + int ap_for_each_sta(struct hostapd_data *hapd, + int (*cb)(struct hostapd_data *hapd, struct sta_info *sta, + void *ctx), +@@ -220,9 +323,13 @@ + struct sta_info *sta, void *ctx); + #endif /* CONFIG_WPS */ + int ap_sta_bind_vlan(struct hostapd_data *hapd, struct sta_info *sta); ++int ap_sta_set_vlan(struct hostapd_data *hapd, struct sta_info *sta, ++ struct vlan_description *vlan_desc); + void ap_sta_start_sa_query(struct hostapd_data *hapd, struct sta_info *sta); + void ap_sta_stop_sa_query(struct hostapd_data *hapd, struct sta_info *sta); + int ap_check_sa_query_timeout(struct hostapd_data *hapd, struct sta_info *sta); ++const char * ap_sta_wpa_get_keyid(struct hostapd_data *hapd, ++ struct sta_info *sta); + void ap_sta_disconnect(struct hostapd_data *hapd, struct sta_info *sta, + const u8 *addr, u16 reason); + +@@ -235,7 +342,13 @@ + + void ap_sta_deauth_cb(struct hostapd_data *hapd, struct sta_info *sta); + void ap_sta_disassoc_cb(struct hostapd_data *hapd, struct sta_info *sta); ++void ap_sta_clear_disconnect_timeouts(struct hostapd_data *hapd, ++ struct sta_info *sta); + + int ap_sta_flags_txt(u32 flags, char *buf, size_t buflen); ++void ap_sta_delayed_1x_auth_fail_disconnect(struct hostapd_data *hapd, ++ struct sta_info *sta); ++int ap_sta_pending_delayed_1x_auth_fail_disconnect(struct hostapd_data *hapd, ++ struct sta_info *sta); + + #endif /* STA_INFO_H */ +--- contrib/wpa/src/ap/taxonomy.c.orig ++++ contrib/wpa/src/ap/taxonomy.c +@@ -0,0 +1,292 @@ ++/* ++ * hostapd / Client taxonomy ++ * Copyright (c) 2015 Google, Inc. ++ * ++ * This software may be distributed under the terms of the BSD license. ++ * See README for more details. ++ * ++ * Parse a series of IEs, as in Probe Request or (Re)Association Request frames, ++ * and render them to a descriptive string. The tag number of standard options ++ * is written to the string, while the vendor ID and subtag are written for ++ * vendor options. ++ * ++ * Example strings: ++ * 0,1,50,45,221(00904c,51) ++ * 0,1,33,36,48,45,221(00904c,51),221(0050f2,2) ++ */ ++ ++#include "utils/includes.h" ++ ++#include "utils/common.h" ++#include "common/wpa_ctrl.h" ++#include "hostapd.h" ++#include "sta_info.h" ++#include "taxonomy.h" ++ ++ ++/* Copy a string with no funny schtuff allowed; only alphanumerics. */ ++static void no_mischief_strncpy(char *dst, const char *src, size_t n) ++{ ++ size_t i; ++ ++ for (i = 0; i < n; i++) { ++ unsigned char s = src[i]; ++ int is_lower = s >= 'a' && s <= 'z'; ++ int is_upper = s >= 'A' && s <= 'Z'; ++ int is_digit = s >= '0' && s <= '9'; ++ ++ if (is_lower || is_upper || is_digit) { ++ /* TODO: if any manufacturer uses Unicode within the ++ * WPS header, it will get mangled here. */ ++ dst[i] = s; ++ } else { ++ /* Note that even spaces will be transformed to ++ * underscores, so 'Nexus 7' will turn into 'Nexus_7'. ++ * This is deliberate, to make the string easier to ++ * parse. */ ++ dst[i] = '_'; ++ } ++ } ++} ++ ++ ++static int get_wps_name(char *name, size_t name_len, ++ const u8 *data, size_t data_len) ++{ ++ /* Inside the WPS IE are a series of attributes, using two byte IDs ++ * and two byte lengths. We're looking for the model name, if ++ * present. */ ++ while (data_len >= 4) { ++ u16 id, elen; ++ ++ id = WPA_GET_BE16(data); ++ elen = WPA_GET_BE16(data + 2); ++ data += 4; ++ data_len -= 4; ++ ++ if (elen > data_len) ++ return 0; ++ ++ if (id == 0x1023) { ++ /* Model name, like 'Nexus 7' */ ++ size_t n = (elen < name_len) ? elen : name_len; ++ no_mischief_strncpy(name, (const char *) data, n); ++ return n; ++ } ++ ++ data += elen; ++ data_len -= elen; ++ } ++ ++ return 0; ++} ++ ++ ++static void ie_to_string(char *fstr, size_t fstr_len, const struct wpabuf *ies) ++{ ++ char *fpos = fstr; ++ char *fend = fstr + fstr_len; ++ char htcap[7 + 4 + 1]; /* ",htcap:" + %04hx + trailing NUL */ ++ char htagg[7 + 2 + 1]; /* ",htagg:" + %02hx + trailing NUL */ ++ char htmcs[7 + 8 + 1]; /* ",htmcs:" + %08x + trailing NUL */ ++ char vhtcap[8 + 8 + 1]; /* ",vhtcap:" + %08x + trailing NUL */ ++ char vhtrxmcs[10 + 8 + 1]; /* ",vhtrxmcs:" + %08x + trailing NUL */ ++ char vhttxmcs[10 + 8 + 1]; /* ",vhttxmcs:" + %08x + trailing NUL */ ++#define MAX_EXTCAP 254 ++ char extcap[8 + 2 * MAX_EXTCAP + 1]; /* ",extcap:" + hex + trailing NUL ++ */ ++ char txpow[7 + 4 + 1]; /* ",txpow:" + %04hx + trailing NUL */ ++#define WPS_NAME_LEN 32 ++ char wps[WPS_NAME_LEN + 5 + 1]; /* room to prepend ",wps:" + trailing ++ * NUL */ ++ int num = 0; ++ const u8 *ie; ++ size_t ie_len; ++ int ret; ++ ++ os_memset(htcap, 0, sizeof(htcap)); ++ os_memset(htagg, 0, sizeof(htagg)); ++ os_memset(htmcs, 0, sizeof(htmcs)); ++ os_memset(vhtcap, 0, sizeof(vhtcap)); ++ os_memset(vhtrxmcs, 0, sizeof(vhtrxmcs)); ++ os_memset(vhttxmcs, 0, sizeof(vhttxmcs)); ++ os_memset(extcap, 0, sizeof(extcap)); ++ os_memset(txpow, 0, sizeof(txpow)); ++ os_memset(wps, 0, sizeof(wps)); ++ *fpos = '\0'; ++ ++ if (!ies) ++ return; ++ ie = wpabuf_head(ies); ++ ie_len = wpabuf_len(ies); ++ ++ while (ie_len >= 2) { ++ u8 id, elen; ++ char *sep = (num++ == 0) ? "" : ","; ++ ++ id = *ie++; ++ elen = *ie++; ++ ie_len -= 2; ++ ++ if (elen > ie_len) ++ break; ++ ++ if (id == WLAN_EID_VENDOR_SPECIFIC && elen >= 4) { ++ /* Vendor specific */ ++ if (WPA_GET_BE32(ie) == WPS_IE_VENDOR_TYPE) { ++ /* WPS */ ++ char model_name[WPS_NAME_LEN + 1]; ++ const u8 *data = &ie[4]; ++ size_t data_len = elen - 4; ++ ++ os_memset(model_name, 0, sizeof(model_name)); ++ if (get_wps_name(model_name, WPS_NAME_LEN, data, ++ data_len)) { ++ os_snprintf(wps, sizeof(wps), ++ ",wps:%s", model_name); ++ } ++ } ++ ++ ret = os_snprintf(fpos, fend - fpos, ++ "%s%d(%02x%02x%02x,%d)", ++ sep, id, ie[0], ie[1], ie[2], ie[3]); ++ } else { ++ if (id == WLAN_EID_HT_CAP && elen >= 2) { ++ /* HT Capabilities (802.11n) */ ++ os_snprintf(htcap, sizeof(htcap), ++ ",htcap:%04hx", ++ WPA_GET_LE16(ie)); ++ } ++ if (id == WLAN_EID_HT_CAP && elen >= 3) { ++ /* HT Capabilities (802.11n), A-MPDU information ++ */ ++ os_snprintf(htagg, sizeof(htagg), ++ ",htagg:%02hx", (u16) ie[2]); ++ } ++ if (id == WLAN_EID_HT_CAP && elen >= 7) { ++ /* HT Capabilities (802.11n), MCS information */ ++ os_snprintf(htmcs, sizeof(htmcs), ++ ",htmcs:%08hx", ++ (u16) WPA_GET_LE32(ie + 3)); ++ } ++ if (id == WLAN_EID_VHT_CAP && elen >= 4) { ++ /* VHT Capabilities (802.11ac) */ ++ os_snprintf(vhtcap, sizeof(vhtcap), ++ ",vhtcap:%08x", ++ WPA_GET_LE32(ie)); ++ } ++ if (id == WLAN_EID_VHT_CAP && elen >= 8) { ++ /* VHT Capabilities (802.11ac), RX MCS ++ * information */ ++ os_snprintf(vhtrxmcs, sizeof(vhtrxmcs), ++ ",vhtrxmcs:%08x", ++ WPA_GET_LE32(ie + 4)); ++ } ++ if (id == WLAN_EID_VHT_CAP && elen >= 12) { ++ /* VHT Capabilities (802.11ac), TX MCS ++ * information */ ++ os_snprintf(vhttxmcs, sizeof(vhttxmcs), ++ ",vhttxmcs:%08x", ++ WPA_GET_LE32(ie + 8)); ++ } ++ if (id == WLAN_EID_EXT_CAPAB) { ++ /* Extended Capabilities */ ++ int i; ++ int len = (elen < MAX_EXTCAP) ? elen : ++ MAX_EXTCAP; ++ char *p = extcap; ++ ++ p += os_snprintf(extcap, sizeof(extcap), ++ ",extcap:"); ++ for (i = 0; i < len; i++) { ++ int lim; ++ ++ lim = sizeof(extcap) - ++ os_strlen(extcap); ++ if (lim <= 0) ++ break; ++ p += os_snprintf(p, lim, "%02x", ++ *(ie + i)); ++ } ++ } ++ if (id == WLAN_EID_PWR_CAPABILITY && elen == 2) { ++ /* TX Power */ ++ os_snprintf(txpow, sizeof(txpow), ++ ",txpow:%04hx", ++ WPA_GET_LE16(ie)); ++ } ++ ++ ret = os_snprintf(fpos, fend - fpos, "%s%d", sep, id); ++ } ++ if (os_snprintf_error(fend - fpos, ret)) ++ goto fail; ++ fpos += ret; ++ ++ ie += elen; ++ ie_len -= elen; ++ } ++ ++ ret = os_snprintf(fpos, fend - fpos, "%s%s%s%s%s%s%s%s%s", ++ htcap, htagg, htmcs, vhtcap, vhtrxmcs, vhttxmcs, ++ txpow, extcap, wps); ++ if (os_snprintf_error(fend - fpos, ret)) { ++ fail: ++ fstr[0] = '\0'; ++ } ++} ++ ++ ++int retrieve_sta_taxonomy(const struct hostapd_data *hapd, ++ struct sta_info *sta, char *buf, size_t buflen) ++{ ++ int ret; ++ char *pos, *end; ++ ++ if (!sta->probe_ie_taxonomy || !sta->assoc_ie_taxonomy) ++ return 0; ++ ++ ret = os_snprintf(buf, buflen, "wifi4|probe:"); ++ if (os_snprintf_error(buflen, ret)) ++ return 0; ++ pos = buf + ret; ++ end = buf + buflen; ++ ++ ie_to_string(pos, end - pos, sta->probe_ie_taxonomy); ++ pos = os_strchr(pos, '\0'); ++ if (pos >= end) ++ return 0; ++ ret = os_snprintf(pos, end - pos, "|assoc:"); ++ if (os_snprintf_error(end - pos, ret)) ++ return 0; ++ pos += ret; ++ ie_to_string(pos, end - pos, sta->assoc_ie_taxonomy); ++ pos = os_strchr(pos, '\0'); ++ return pos - buf; ++} ++ ++ ++void taxonomy_sta_info_probe_req(const struct hostapd_data *hapd, ++ struct sta_info *sta, ++ const u8 *ie, size_t ie_len) ++{ ++ wpabuf_free(sta->probe_ie_taxonomy); ++ sta->probe_ie_taxonomy = wpabuf_alloc_copy(ie, ie_len); ++} ++ ++ ++void taxonomy_hostapd_sta_info_probe_req(const struct hostapd_data *hapd, ++ struct hostapd_sta_info *info, ++ const u8 *ie, size_t ie_len) ++{ ++ wpabuf_free(info->probe_ie_taxonomy); ++ info->probe_ie_taxonomy = wpabuf_alloc_copy(ie, ie_len); ++} ++ ++ ++void taxonomy_sta_info_assoc_req(const struct hostapd_data *hapd, ++ struct sta_info *sta, ++ const u8 *ie, size_t ie_len) ++{ ++ wpabuf_free(sta->assoc_ie_taxonomy); ++ sta->assoc_ie_taxonomy = wpabuf_alloc_copy(ie, ie_len); ++} +--- contrib/wpa/src/ap/taxonomy.h.orig ++++ contrib/wpa/src/ap/taxonomy.h +@@ -0,0 +1,24 @@ ++/* ++ * hostapd / Station client taxonomy ++ * Copyright (c) 2015 Google, Inc. ++ * ++ * This software may be distributed under the terms of the BSD license. ++ * See README for more details. ++ */ ++ ++#ifndef TAXONOMY_H ++#define TAXONOMY_H ++ ++void taxonomy_sta_info_probe_req(const struct hostapd_data *hapd, ++ struct sta_info *sta, ++ const u8 *ie, size_t ie_len); ++void taxonomy_hostapd_sta_info_probe_req(const struct hostapd_data *hapd, ++ struct hostapd_sta_info *sta, ++ const u8 *ie, size_t ie_len); ++void taxonomy_sta_info_assoc_req(const struct hostapd_data *hapd, ++ struct sta_info *sta, ++ const u8 *ie, size_t ie_len); ++int retrieve_sta_taxonomy(const struct hostapd_data *hapd, ++ struct sta_info *sta, char *buf, size_t buflen); ++ ++#endif /* TAXONOMY_H */ +--- contrib/wpa/src/ap/tkip_countermeasures.c.orig ++++ contrib/wpa/src/ap/tkip_countermeasures.c +@@ -71,6 +71,11 @@ + struct os_reltime now; + int ret = 0; + ++ hostapd_logger(hapd, addr, HOSTAPD_MODULE_IEEE80211, ++ HOSTAPD_LEVEL_INFO, ++ "Michael MIC failure detected in received frame%s", ++ local ? " (local)" : ""); ++ + if (addr && local) { + struct sta_info *sta = ap_get_sta(hapd, addr); + if (sta != NULL) { +--- contrib/wpa/src/ap/vlan.c.orig ++++ contrib/wpa/src/ap/vlan.c +@@ -0,0 +1,34 @@ ++/* ++ * hostapd / VLAN definition ++ * Copyright (c) 2016, 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 "ap/vlan.h" ++ ++/* compare the two arguments, NULL is treated as empty ++ * return zero iff they are equal ++ */ ++int vlan_compare(struct vlan_description *a, struct vlan_description *b) ++{ ++ int i; ++ const int a_empty = !a || !a->notempty; ++ const int b_empty = !b || !b->notempty; ++ ++ if (a_empty && b_empty) ++ return 0; ++ if (a_empty || b_empty) ++ return 1; ++ if (a->untagged != b->untagged) ++ return 1; ++ for (i = 0; i < MAX_NUM_TAGGED_VLAN; i++) { ++ if (a->tagged[i] != b->tagged[i]) ++ return 1; ++ } ++ return 0; ++} +--- contrib/wpa/src/ap/vlan.h.orig ++++ contrib/wpa/src/ap/vlan.h +@@ -0,0 +1,30 @@ ++/* ++ * hostapd / VLAN definition ++ * Copyright (c) 2015, Jouni Malinen ++ * ++ * This software may be distributed under the terms of the BSD license. ++ * See README for more details. ++ */ ++ ++#ifndef VLAN_H ++#define VLAN_H ++ ++#define MAX_NUM_TAGGED_VLAN 32 ++ ++struct vlan_description { ++ int notempty; /* 0 : no vlan information present, 1: else */ ++ int untagged; /* >0 802.1q vid */ ++ int tagged[MAX_NUM_TAGGED_VLAN]; /* first k items, ascending order */ ++}; ++ ++#ifndef CONFIG_NO_VLAN ++int vlan_compare(struct vlan_description *a, struct vlan_description *b); ++#else /* CONFIG_NO_VLAN */ ++static inline int ++vlan_compare(struct vlan_description *a, struct vlan_description *b) ++{ ++ return 0; ++} ++#endif /* CONFIG_NO_VLAN */ ++ ++#endif /* VLAN_H */ +--- contrib/wpa/src/ap/vlan_full.c.orig ++++ contrib/wpa/src/ap/vlan_full.c +@@ -0,0 +1,801 @@ ++/* ++ * hostapd / VLAN initialization - full dynamic VLAN ++ * Copyright 2003, Instant802 Networks, Inc. ++ * Copyright 2005-2006, Devicescape Software, Inc. ++ * Copyright (c) 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 ++/* Avoid conflicts due to NetBSD net/if.h if_type define with driver.h */ ++#undef if_type ++#include ++ ++#include "utils/common.h" ++#include "drivers/priv_netlink.h" ++#include "drivers/linux_ioctl.h" ++#include "common/linux_bridge.h" ++#include "common/linux_vlan.h" ++#include "utils/eloop.h" ++#include "hostapd.h" ++#include "ap_config.h" ++#include "ap_drv_ops.h" ++#include "wpa_auth.h" ++#include "vlan_init.h" ++#include "vlan_util.h" ++ ++ ++struct full_dynamic_vlan { ++ int s; /* socket on which to listen for new/removed interfaces. */ ++}; ++ ++#define DVLAN_CLEAN_BR 0x1 ++#define DVLAN_CLEAN_VLAN 0x2 ++#define DVLAN_CLEAN_VLAN_PORT 0x4 ++ ++struct dynamic_iface { ++ char ifname[IFNAMSIZ + 1]; ++ int usage; ++ int clean; ++ struct dynamic_iface *next; ++}; ++ ++ ++/* Increment ref counter for ifname and add clean flag. ++ * If not in list, add it only if some flags are given. ++ */ ++static void dyn_iface_get(struct hostapd_data *hapd, const char *ifname, ++ int clean) ++{ ++ struct dynamic_iface *next, **dynamic_ifaces; ++ struct hapd_interfaces *interfaces; ++ ++ interfaces = hapd->iface->interfaces; ++ dynamic_ifaces = &interfaces->vlan_priv; ++ ++ for (next = *dynamic_ifaces; next; next = next->next) { ++ if (os_strcmp(ifname, next->ifname) == 0) ++ break; ++ } ++ ++ if (next) { ++ next->usage++; ++ next->clean |= clean; ++ return; ++ } ++ ++ if (!clean) ++ return; ++ ++ next = os_zalloc(sizeof(*next)); ++ if (!next) ++ return; ++ os_strlcpy(next->ifname, ifname, sizeof(next->ifname)); ++ next->usage = 1; ++ next->clean = clean; ++ next->next = *dynamic_ifaces; ++ *dynamic_ifaces = next; ++} ++ ++ ++/* Decrement reference counter for given ifname. ++ * Return clean flag iff reference counter was decreased to zero, else zero ++ */ ++static int dyn_iface_put(struct hostapd_data *hapd, const char *ifname) ++{ ++ struct dynamic_iface *next, *prev = NULL, **dynamic_ifaces; ++ struct hapd_interfaces *interfaces; ++ int clean; ++ ++ interfaces = hapd->iface->interfaces; ++ dynamic_ifaces = &interfaces->vlan_priv; ++ ++ for (next = *dynamic_ifaces; next; next = next->next) { ++ if (os_strcmp(ifname, next->ifname) == 0) ++ break; ++ prev = next; ++ } ++ ++ if (!next) ++ return 0; ++ ++ next->usage--; ++ if (next->usage) ++ return 0; ++ ++ if (prev) ++ prev->next = next->next; ++ else ++ *dynamic_ifaces = next->next; ++ clean = next->clean; ++ os_free(next); ++ ++ return clean; ++} ++ ++ ++static int ifconfig_down(const char *if_name) ++{ ++ wpa_printf(MSG_DEBUG, "VLAN: Set interface %s down", if_name); ++ return ifconfig_helper(if_name, 0); ++} ++ ++ ++/* This value should be 256 ONLY. If it is something else, then hostapd ++ * might crash!, as this value has been hard-coded in 2.4.x kernel ++ * bridging code. ++ */ ++#define MAX_BR_PORTS 256 ++ ++static int br_delif(const char *br_name, const char *if_name) ++{ ++ int fd; ++ struct ifreq ifr; ++ unsigned long args[2]; ++ int if_index; ++ ++ wpa_printf(MSG_DEBUG, "VLAN: br_delif(%s, %s)", br_name, if_name); ++ if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { ++ wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) " ++ "failed: %s", __func__, strerror(errno)); ++ return -1; ++ } ++ ++ if (linux_br_del_if(fd, br_name, if_name) == 0) ++ goto done; ++ ++ if_index = if_nametoindex(if_name); ++ ++ if (if_index == 0) { ++ wpa_printf(MSG_ERROR, "VLAN: %s: Failure determining " ++ "interface index for '%s'", ++ __func__, if_name); ++ close(fd); ++ return -1; ++ } ++ ++ args[0] = BRCTL_DEL_IF; ++ args[1] = if_index; ++ ++ os_strlcpy(ifr.ifr_name, br_name, sizeof(ifr.ifr_name)); ++ ifr.ifr_data = (void *) args; ++ ++ if (ioctl(fd, SIOCDEVPRIVATE, &ifr) < 0 && errno != EINVAL) { ++ /* No error if interface already removed. */ ++ wpa_printf(MSG_ERROR, "VLAN: %s: ioctl[SIOCDEVPRIVATE," ++ "BRCTL_DEL_IF] failed for br_name=%s if_name=%s: " ++ "%s", __func__, br_name, if_name, strerror(errno)); ++ close(fd); ++ return -1; ++ } ++ ++done: ++ close(fd); ++ return 0; ++} ++ ++ ++/* ++ Add interface 'if_name' to the bridge 'br_name' ++ ++ returns -1 on error ++ returns 1 if the interface is already part of the bridge ++ returns 0 otherwise ++*/ ++static int br_addif(const char *br_name, const char *if_name) ++{ ++ int fd; ++ struct ifreq ifr; ++ unsigned long args[2]; ++ int if_index; ++ ++ wpa_printf(MSG_DEBUG, "VLAN: br_addif(%s, %s)", br_name, if_name); ++ if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { ++ wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) " ++ "failed: %s", __func__, strerror(errno)); ++ return -1; ++ } ++ ++ if (linux_br_add_if(fd, br_name, if_name) == 0) ++ goto done; ++ if (errno == EBUSY) { ++ /* The interface is already added. */ ++ close(fd); ++ return 1; ++ } ++ ++ if_index = if_nametoindex(if_name); ++ ++ if (if_index == 0) { ++ wpa_printf(MSG_ERROR, "VLAN: %s: Failure determining " ++ "interface index for '%s'", ++ __func__, if_name); ++ close(fd); ++ return -1; ++ } ++ ++ args[0] = BRCTL_ADD_IF; ++ args[1] = if_index; ++ ++ os_strlcpy(ifr.ifr_name, br_name, sizeof(ifr.ifr_name)); ++ ifr.ifr_data = (void *) args; ++ ++ if (ioctl(fd, SIOCDEVPRIVATE, &ifr) < 0) { ++ if (errno == EBUSY) { ++ /* The interface is already added. */ ++ close(fd); ++ return 1; ++ } ++ ++ wpa_printf(MSG_ERROR, "VLAN: %s: ioctl[SIOCDEVPRIVATE," ++ "BRCTL_ADD_IF] failed for br_name=%s if_name=%s: " ++ "%s", __func__, br_name, if_name, strerror(errno)); ++ close(fd); ++ return -1; ++ } ++ ++done: ++ close(fd); ++ return 0; ++} ++ ++ ++static int br_delbr(const char *br_name) ++{ ++ int fd; ++ unsigned long arg[2]; ++ ++ wpa_printf(MSG_DEBUG, "VLAN: br_delbr(%s)", br_name); ++ if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { ++ wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) " ++ "failed: %s", __func__, strerror(errno)); ++ return -1; ++ } ++ ++ if (linux_br_del(fd, br_name) == 0) ++ goto done; ++ ++ arg[0] = BRCTL_DEL_BRIDGE; ++ arg[1] = (unsigned long) br_name; ++ ++ if (ioctl(fd, SIOCGIFBR, arg) < 0 && errno != ENXIO) { ++ /* No error if bridge already removed. */ ++ wpa_printf(MSG_ERROR, "VLAN: %s: BRCTL_DEL_BRIDGE failed for " ++ "%s: %s", __func__, br_name, strerror(errno)); ++ close(fd); ++ return -1; ++ } ++ ++done: ++ close(fd); ++ return 0; ++} ++ ++ ++/* ++ Add a bridge with the name 'br_name'. ++ ++ returns -1 on error ++ returns 1 if the bridge already exists ++ returns 0 otherwise ++*/ ++static int br_addbr(const char *br_name) ++{ ++ int fd; ++ unsigned long arg[4]; ++ struct ifreq ifr; ++ ++ wpa_printf(MSG_DEBUG, "VLAN: br_addbr(%s)", br_name); ++ if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { ++ wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) " ++ "failed: %s", __func__, strerror(errno)); ++ return -1; ++ } ++ ++ if (linux_br_add(fd, br_name) == 0) ++ goto done; ++ if (errno == EEXIST) { ++ /* The bridge is already added. */ ++ close(fd); ++ return 1; ++ } ++ ++ arg[0] = BRCTL_ADD_BRIDGE; ++ arg[1] = (unsigned long) br_name; ++ ++ if (ioctl(fd, SIOCGIFBR, arg) < 0) { ++ if (errno == EEXIST) { ++ /* The bridge is already added. */ ++ close(fd); ++ return 1; ++ } else { ++ wpa_printf(MSG_ERROR, "VLAN: %s: BRCTL_ADD_BRIDGE " ++ "failed for %s: %s", ++ __func__, br_name, strerror(errno)); ++ close(fd); ++ return -1; ++ } ++ } ++ ++done: ++ /* Decrease forwarding delay to avoid EAPOL timeouts. */ ++ os_memset(&ifr, 0, sizeof(ifr)); ++ os_strlcpy(ifr.ifr_name, br_name, IFNAMSIZ); ++ arg[0] = BRCTL_SET_BRIDGE_FORWARD_DELAY; ++ arg[1] = 1; ++ arg[2] = 0; ++ arg[3] = 0; ++ ifr.ifr_data = (char *) &arg; ++ if (ioctl(fd, SIOCDEVPRIVATE, &ifr) < 0) { ++ wpa_printf(MSG_ERROR, "VLAN: %s: " ++ "BRCTL_SET_BRIDGE_FORWARD_DELAY (1 sec) failed for " ++ "%s: %s", __func__, br_name, strerror(errno)); ++ /* Continue anyway */ ++ } ++ ++ close(fd); ++ return 0; ++} ++ ++ ++static int br_getnumports(const char *br_name) ++{ ++ int fd; ++ int i; ++ int port_cnt = 0; ++ unsigned long arg[4]; ++ int ifindices[MAX_BR_PORTS]; ++ struct ifreq ifr; ++ ++ if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { ++ wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) " ++ "failed: %s", __func__, strerror(errno)); ++ return -1; ++ } ++ ++ arg[0] = BRCTL_GET_PORT_LIST; ++ arg[1] = (unsigned long) ifindices; ++ arg[2] = MAX_BR_PORTS; ++ arg[3] = 0; ++ ++ os_memset(ifindices, 0, sizeof(ifindices)); ++ os_strlcpy(ifr.ifr_name, br_name, sizeof(ifr.ifr_name)); ++ ifr.ifr_data = (void *) arg; ++ ++ if (ioctl(fd, SIOCDEVPRIVATE, &ifr) < 0) { ++ wpa_printf(MSG_ERROR, "VLAN: %s: BRCTL_GET_PORT_LIST " ++ "failed for %s: %s", ++ __func__, br_name, strerror(errno)); ++ close(fd); ++ return -1; ++ } ++ ++ for (i = 1; i < MAX_BR_PORTS; i++) { ++ if (ifindices[i] > 0) { ++ port_cnt++; ++ } ++ } ++ ++ close(fd); ++ return port_cnt; ++} ++ ++ ++static void vlan_newlink_tagged(int vlan_naming, const char *tagged_interface, ++ const char *br_name, int vid, ++ struct hostapd_data *hapd) ++{ ++ char vlan_ifname[IFNAMSIZ]; ++ int clean; ++ int ret; ++ ++ if (vlan_naming == DYNAMIC_VLAN_NAMING_WITH_DEVICE) ++ ret = os_snprintf(vlan_ifname, sizeof(vlan_ifname), "%s.%d", ++ tagged_interface, vid); ++ else ++ ret = os_snprintf(vlan_ifname, sizeof(vlan_ifname), "vlan%d", ++ vid); ++ if (ret >= (int) sizeof(vlan_ifname)) ++ wpa_printf(MSG_WARNING, ++ "VLAN: Interface name was truncated to %s", ++ vlan_ifname); ++ ++ clean = 0; ++ ifconfig_up(tagged_interface); ++ if (!vlan_add(tagged_interface, vid, vlan_ifname)) ++ clean |= DVLAN_CLEAN_VLAN; ++ ++ if (!br_addif(br_name, vlan_ifname)) ++ clean |= DVLAN_CLEAN_VLAN_PORT; ++ ++ dyn_iface_get(hapd, vlan_ifname, clean); ++ ++ ifconfig_up(vlan_ifname); ++} ++ ++ ++static void vlan_bridge_name(char *br_name, struct hostapd_data *hapd, ++ struct hostapd_vlan *vlan, int vid) ++{ ++ char *tagged_interface = hapd->conf->ssid.vlan_tagged_interface; ++ int ret; ++ ++ if (vlan->bridge[0]) { ++ os_strlcpy(br_name, vlan->bridge, IFNAMSIZ); ++ ret = 0; ++ } else if (hapd->conf->vlan_bridge[0]) { ++ ret = os_snprintf(br_name, IFNAMSIZ, "%s%d", ++ hapd->conf->vlan_bridge, vid); ++ } else if (tagged_interface) { ++ ret = os_snprintf(br_name, IFNAMSIZ, "br%s.%d", ++ tagged_interface, vid); ++ } else { ++ ret = os_snprintf(br_name, IFNAMSIZ, "brvlan%d", vid); ++ } ++ if (ret >= IFNAMSIZ) ++ wpa_printf(MSG_WARNING, ++ "VLAN: Interface name was truncated to %s", ++ br_name); ++} ++ ++ ++static void vlan_get_bridge(const char *br_name, struct hostapd_data *hapd, ++ int vid) ++{ ++ char *tagged_interface = hapd->conf->ssid.vlan_tagged_interface; ++ int vlan_naming = hapd->conf->ssid.vlan_naming; ++ ++ dyn_iface_get(hapd, br_name, br_addbr(br_name) ? 0 : DVLAN_CLEAN_BR); ++ ++ ifconfig_up(br_name); ++ ++ if (tagged_interface) ++ vlan_newlink_tagged(vlan_naming, tagged_interface, br_name, ++ vid, hapd); ++} ++ ++ ++void vlan_newlink(const char *ifname, struct hostapd_data *hapd) ++{ ++ char br_name[IFNAMSIZ]; ++ struct hostapd_vlan *vlan; ++ int untagged, *tagged, i, notempty; ++ ++ wpa_printf(MSG_DEBUG, "VLAN: vlan_newlink(%s)", ifname); ++ ++ for (vlan = hapd->conf->vlan; vlan; vlan = vlan->next) { ++ if (vlan->configured || ++ os_strcmp(ifname, vlan->ifname) != 0) ++ continue; ++ break; ++ } ++ if (!vlan) ++ return; ++ ++ vlan->configured = 1; ++ ++ notempty = vlan->vlan_desc.notempty; ++ untagged = vlan->vlan_desc.untagged; ++ tagged = vlan->vlan_desc.tagged; ++ ++ if (!notempty) { ++ /* Non-VLAN STA */ ++ if (hapd->conf->bridge[0] && ++ !br_addif(hapd->conf->bridge, ifname)) ++ vlan->clean |= DVLAN_CLEAN_WLAN_PORT; ++ } else if (untagged > 0 && untagged <= MAX_VLAN_ID) { ++ vlan_bridge_name(br_name, hapd, vlan, untagged); ++ ++ vlan_get_bridge(br_name, hapd, untagged); ++ ++ if (!br_addif(br_name, ifname)) ++ vlan->clean |= DVLAN_CLEAN_WLAN_PORT; ++ } ++ ++ for (i = 0; i < MAX_NUM_TAGGED_VLAN && tagged[i]; i++) { ++ if (tagged[i] == untagged || ++ tagged[i] <= 0 || tagged[i] > MAX_VLAN_ID || ++ (i > 0 && tagged[i] == tagged[i - 1])) ++ continue; ++ vlan_bridge_name(br_name, hapd, vlan, tagged[i]); ++ vlan_get_bridge(br_name, hapd, tagged[i]); ++ vlan_newlink_tagged(DYNAMIC_VLAN_NAMING_WITH_DEVICE, ++ ifname, br_name, tagged[i], hapd); ++ } ++ ++ ifconfig_up(ifname); ++} ++ ++ ++static void vlan_dellink_tagged(int vlan_naming, const char *tagged_interface, ++ const char *br_name, int vid, ++ struct hostapd_data *hapd) ++{ ++ char vlan_ifname[IFNAMSIZ]; ++ int clean; ++ int ret; ++ ++ if (vlan_naming == DYNAMIC_VLAN_NAMING_WITH_DEVICE) ++ ret = os_snprintf(vlan_ifname, sizeof(vlan_ifname), "%s.%d", ++ tagged_interface, vid); ++ else ++ ret = os_snprintf(vlan_ifname, sizeof(vlan_ifname), "vlan%d", ++ vid); ++ if (ret >= (int) sizeof(vlan_ifname)) ++ wpa_printf(MSG_WARNING, ++ "VLAN: Interface name was truncated to %s", ++ vlan_ifname); ++ ++ ++ clean = dyn_iface_put(hapd, vlan_ifname); ++ ++ if (clean & DVLAN_CLEAN_VLAN_PORT) ++ br_delif(br_name, vlan_ifname); ++ ++ if (clean & DVLAN_CLEAN_VLAN) { ++ ifconfig_down(vlan_ifname); ++ vlan_rem(vlan_ifname); ++ } ++} ++ ++ ++static void vlan_put_bridge(const char *br_name, struct hostapd_data *hapd, ++ int vid) ++{ ++ int clean; ++ char *tagged_interface = hapd->conf->ssid.vlan_tagged_interface; ++ int vlan_naming = hapd->conf->ssid.vlan_naming; ++ ++ if (tagged_interface) ++ vlan_dellink_tagged(vlan_naming, tagged_interface, br_name, ++ vid, hapd); ++ ++ clean = dyn_iface_put(hapd, br_name); ++ if ((clean & DVLAN_CLEAN_BR) && br_getnumports(br_name) == 0) { ++ ifconfig_down(br_name); ++ br_delbr(br_name); ++ } ++} ++ ++ ++void vlan_dellink(const char *ifname, struct hostapd_data *hapd) ++{ ++ struct hostapd_vlan *first, *prev, *vlan = hapd->conf->vlan; ++ ++ wpa_printf(MSG_DEBUG, "VLAN: vlan_dellink(%s)", ifname); ++ ++ first = prev = vlan; ++ ++ while (vlan) { ++ if (os_strcmp(ifname, vlan->ifname) != 0) { ++ prev = vlan; ++ vlan = vlan->next; ++ continue; ++ } ++ break; ++ } ++ if (!vlan) ++ return; ++ ++ if (vlan->configured) { ++ int notempty = vlan->vlan_desc.notempty; ++ int untagged = vlan->vlan_desc.untagged; ++ int *tagged = vlan->vlan_desc.tagged; ++ char br_name[IFNAMSIZ]; ++ int i; ++ ++ for (i = 0; i < MAX_NUM_TAGGED_VLAN && tagged[i]; i++) { ++ if (tagged[i] == untagged || ++ tagged[i] <= 0 || tagged[i] > MAX_VLAN_ID || ++ (i > 0 && tagged[i] == tagged[i - 1])) ++ continue; ++ vlan_bridge_name(br_name, hapd, vlan, tagged[i]); ++ vlan_dellink_tagged(DYNAMIC_VLAN_NAMING_WITH_DEVICE, ++ ifname, br_name, tagged[i], hapd); ++ vlan_put_bridge(br_name, hapd, tagged[i]); ++ } ++ ++ if (!notempty) { ++ /* Non-VLAN STA */ ++ if (hapd->conf->bridge[0] && ++ (vlan->clean & DVLAN_CLEAN_WLAN_PORT)) ++ br_delif(hapd->conf->bridge, ifname); ++ } else if (untagged > 0 && untagged <= MAX_VLAN_ID) { ++ vlan_bridge_name(br_name, hapd, vlan, untagged); ++ ++ if (vlan->clean & DVLAN_CLEAN_WLAN_PORT) ++ br_delif(br_name, vlan->ifname); ++ ++ vlan_put_bridge(br_name, hapd, untagged); ++ } ++ } ++ ++ /* ++ * Ensure this VLAN interface is actually removed even if ++ * NEWLINK message is only received later. ++ */ ++ if (if_nametoindex(vlan->ifname) && vlan_if_remove(hapd, vlan)) ++ wpa_printf(MSG_ERROR, ++ "VLAN: Could not remove VLAN iface: %s: %s", ++ vlan->ifname, strerror(errno)); ++ ++ if (vlan == first) ++ hapd->conf->vlan = vlan->next; ++ else ++ prev->next = vlan->next; ++ ++ os_free(vlan); ++} ++ ++ ++static void ++vlan_read_ifnames(struct nlmsghdr *h, size_t len, int del, ++ struct hostapd_data *hapd) ++{ ++ struct ifinfomsg *ifi; ++ int attrlen, nlmsg_len, rta_len; ++ struct rtattr *attr; ++ char ifname[IFNAMSIZ + 1]; ++ ++ if (len < sizeof(*ifi)) ++ return; ++ ++ ifi = NLMSG_DATA(h); ++ ++ nlmsg_len = NLMSG_ALIGN(sizeof(struct ifinfomsg)); ++ ++ attrlen = h->nlmsg_len - nlmsg_len; ++ if (attrlen < 0) ++ return; ++ ++ attr = (struct rtattr *) (((char *) ifi) + nlmsg_len); ++ ++ os_memset(ifname, 0, sizeof(ifname)); ++ rta_len = RTA_ALIGN(sizeof(struct rtattr)); ++ while (RTA_OK(attr, attrlen)) { ++ if (attr->rta_type == IFLA_IFNAME) { ++ int n = attr->rta_len - rta_len; ++ if (n < 0) ++ break; ++ ++ if ((size_t) n >= sizeof(ifname)) ++ n = sizeof(ifname) - 1; ++ os_memcpy(ifname, ((char *) attr) + rta_len, n); ++ ++ } ++ ++ attr = RTA_NEXT(attr, attrlen); ++ } ++ ++ if (!ifname[0]) ++ return; ++ if (del && if_nametoindex(ifname)) { ++ /* interface still exists, race condition -> ++ * iface has just been recreated */ ++ return; ++ } ++ ++ wpa_printf(MSG_DEBUG, ++ "VLAN: RTM_%sLINK: ifi_index=%d ifname=%s ifi_family=%d ifi_flags=0x%x (%s%s%s%s)", ++ del ? "DEL" : "NEW", ++ ifi->ifi_index, ifname, ifi->ifi_family, ifi->ifi_flags, ++ (ifi->ifi_flags & IFF_UP) ? "[UP]" : "", ++ (ifi->ifi_flags & IFF_RUNNING) ? "[RUNNING]" : "", ++ (ifi->ifi_flags & IFF_LOWER_UP) ? "[LOWER_UP]" : "", ++ (ifi->ifi_flags & IFF_DORMANT) ? "[DORMANT]" : ""); ++ ++ if (del) ++ vlan_dellink(ifname, hapd); ++ else ++ vlan_newlink(ifname, hapd); ++} ++ ++ ++static void vlan_event_receive(int sock, void *eloop_ctx, void *sock_ctx) ++{ ++ char buf[8192]; ++ int left; ++ struct sockaddr_nl from; ++ socklen_t fromlen; ++ struct nlmsghdr *h; ++ struct hostapd_data *hapd = eloop_ctx; ++ ++ fromlen = sizeof(from); ++ left = recvfrom(sock, buf, sizeof(buf), MSG_DONTWAIT, ++ (struct sockaddr *) &from, &fromlen); ++ if (left < 0) { ++ if (errno != EINTR && errno != EAGAIN) ++ wpa_printf(MSG_ERROR, "VLAN: %s: recvfrom failed: %s", ++ __func__, strerror(errno)); ++ return; ++ } ++ ++ h = (struct nlmsghdr *) buf; ++ while (NLMSG_OK(h, left)) { ++ int len, plen; ++ ++ len = h->nlmsg_len; ++ plen = len - sizeof(*h); ++ if (len > left || plen < 0) { ++ wpa_printf(MSG_DEBUG, "VLAN: Malformed netlink " ++ "message: len=%d left=%d plen=%d", ++ len, left, plen); ++ break; ++ } ++ ++ switch (h->nlmsg_type) { ++ case RTM_NEWLINK: ++ vlan_read_ifnames(h, plen, 0, hapd); ++ break; ++ case RTM_DELLINK: ++ vlan_read_ifnames(h, plen, 1, hapd); ++ break; ++ } ++ ++ h = NLMSG_NEXT(h, left); ++ } ++ ++ if (left > 0) { ++ wpa_printf(MSG_DEBUG, "VLAN: %s: %d extra bytes in the end of " ++ "netlink message", __func__, left); ++ } ++} ++ ++ ++struct full_dynamic_vlan * ++full_dynamic_vlan_init(struct hostapd_data *hapd) ++{ ++ struct sockaddr_nl local; ++ struct full_dynamic_vlan *priv; ++ ++ priv = os_zalloc(sizeof(*priv)); ++ if (priv == NULL) ++ return NULL; ++ ++ vlan_set_name_type(hapd->conf->ssid.vlan_naming == ++ DYNAMIC_VLAN_NAMING_WITH_DEVICE ? ++ VLAN_NAME_TYPE_RAW_PLUS_VID_NO_PAD : ++ VLAN_NAME_TYPE_PLUS_VID_NO_PAD); ++ ++ priv->s = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE); ++ if (priv->s < 0) { ++ wpa_printf(MSG_ERROR, "VLAN: %s: socket(PF_NETLINK,SOCK_RAW," ++ "NETLINK_ROUTE) failed: %s", ++ __func__, strerror(errno)); ++ os_free(priv); ++ return NULL; ++ } ++ ++ os_memset(&local, 0, sizeof(local)); ++ local.nl_family = AF_NETLINK; ++ local.nl_groups = RTMGRP_LINK; ++ if (bind(priv->s, (struct sockaddr *) &local, sizeof(local)) < 0) { ++ wpa_printf(MSG_ERROR, "VLAN: %s: bind(netlink) failed: %s", ++ __func__, strerror(errno)); ++ close(priv->s); ++ os_free(priv); ++ return NULL; ++ } ++ ++ if (eloop_register_read_sock(priv->s, vlan_event_receive, hapd, NULL)) ++ { ++ close(priv->s); ++ os_free(priv); ++ return NULL; ++ } ++ ++ return priv; ++} ++ ++ ++void full_dynamic_vlan_deinit(struct full_dynamic_vlan *priv) ++{ ++ if (priv == NULL) ++ return; ++ eloop_unregister_read_sock(priv->s); ++ close(priv->s); ++ os_free(priv); ++} +--- contrib/wpa/src/ap/vlan_ifconfig.c.orig ++++ contrib/wpa/src/ap/vlan_ifconfig.c +@@ -0,0 +1,69 @@ ++/* ++ * hostapd / VLAN ifconfig helpers ++ * Copyright 2003, Instant802 Networks, Inc. ++ * Copyright 2005-2006, Devicescape Software, Inc. ++ * Copyright (c) 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 ++#include ++ ++#include "utils/common.h" ++#include "vlan_util.h" ++ ++ ++int ifconfig_helper(const char *if_name, int up) ++{ ++ int fd; ++ struct ifreq ifr; ++ ++ if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { ++ wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) " ++ "failed: %s", __func__, strerror(errno)); ++ return -1; ++ } ++ ++ os_memset(&ifr, 0, sizeof(ifr)); ++ os_strlcpy(ifr.ifr_name, if_name, IFNAMSIZ); ++ ++ if (ioctl(fd, SIOCGIFFLAGS, &ifr) != 0) { ++ wpa_printf(MSG_ERROR, "VLAN: %s: ioctl(SIOCGIFFLAGS) failed " ++ "for interface %s: %s", ++ __func__, if_name, strerror(errno)); ++ close(fd); ++ return -1; ++ } ++ ++ if (up) ++ ifr.ifr_flags |= IFF_UP; ++ else ++ ifr.ifr_flags &= ~IFF_UP; ++ ++ if (ioctl(fd, SIOCSIFFLAGS, &ifr) != 0) { ++ wpa_printf(MSG_ERROR, "VLAN: %s: ioctl(SIOCSIFFLAGS) failed " ++ "for interface %s (up=%d): %s", ++ __func__, if_name, up, strerror(errno)); ++ close(fd); ++ return -1; ++ } ++ ++ close(fd); ++ return 0; ++} ++ ++ ++int ifconfig_up(const char *if_name) ++{ ++ wpa_printf(MSG_DEBUG, "VLAN: Set interface %s up", if_name); ++ return ifconfig_helper(if_name, 1); ++} ++ ++ ++int iface_exists(const char *ifname) ++{ ++ return if_nametoindex(ifname); ++} +--- contrib/wpa/src/ap/vlan_init.c.orig ++++ contrib/wpa/src/ap/vlan_init.c +@@ -9,921 +9,88 @@ + */ + + #include "utils/includes.h" +-#ifdef CONFIG_FULL_DYNAMIC_VLAN +-#include +-#include +-#include +-#include +-#include +-#endif /* CONFIG_FULL_DYNAMIC_VLAN */ + + #include "utils/common.h" + #include "hostapd.h" + #include "ap_config.h" + #include "ap_drv_ops.h" ++#include "wpa_auth.h" + #include "vlan_init.h" + #include "vlan_util.h" + + +-#ifdef CONFIG_FULL_DYNAMIC_VLAN +- +-#include "drivers/priv_netlink.h" +-#include "utils/eloop.h" +- +- +-struct full_dynamic_vlan { +- int s; /* socket on which to listen for new/removed interfaces. */ +-}; +- +-#define DVLAN_CLEAN_BR 0x1 +-#define DVLAN_CLEAN_VLAN 0x2 +-#define DVLAN_CLEAN_VLAN_PORT 0x4 +- +-struct dynamic_iface { +- char ifname[IFNAMSIZ + 1]; +- int usage; +- int clean; +- struct dynamic_iface *next; +-}; +- +- +-/* Increment ref counter for ifname and add clean flag. +- * If not in list, add it only if some flags are given. +- */ +-static void dyn_iface_get(struct hostapd_data *hapd, const char *ifname, +- int clean) ++static int vlan_if_add(struct hostapd_data *hapd, struct hostapd_vlan *vlan, ++ int existsok) + { +- struct dynamic_iface *next, **dynamic_ifaces; +- struct hapd_interfaces *interfaces; ++ int ret, i; + +- interfaces = hapd->iface->interfaces; +- dynamic_ifaces = &interfaces->vlan_priv; +- +- for (next = *dynamic_ifaces; next; next = next->next) { +- if (os_strcmp(ifname, next->ifname) == 0) +- break; +- } +- +- if (next) { +- next->usage++; +- next->clean |= clean; +- return; +- } +- +- if (!clean) +- return; +- +- next = os_zalloc(sizeof(*next)); +- if (!next) +- return; +- os_strlcpy(next->ifname, ifname, sizeof(next->ifname)); +- next->usage = 1; +- next->clean = clean; +- next->next = *dynamic_ifaces; +- *dynamic_ifaces = next; +-} +- +- +-/* Decrement reference counter for given ifname. +- * Return clean flag iff reference counter was decreased to zero, else zero +- */ +-static int dyn_iface_put(struct hostapd_data *hapd, const char *ifname) +-{ +- struct dynamic_iface *next, *prev = NULL, **dynamic_ifaces; +- struct hapd_interfaces *interfaces; +- int clean; +- +- interfaces = hapd->iface->interfaces; +- dynamic_ifaces = &interfaces->vlan_priv; +- +- for (next = *dynamic_ifaces; next; next = next->next) { +- if (os_strcmp(ifname, next->ifname) == 0) +- break; +- prev = next; +- } +- +- if (!next) +- return 0; +- +- next->usage--; +- if (next->usage) +- return 0; +- +- if (prev) +- prev->next = next->next; +- else +- *dynamic_ifaces = next->next; +- clean = next->clean; +- os_free(next); +- +- return clean; +-} +- +- +-static int ifconfig_helper(const char *if_name, int up) +-{ +- int fd; +- struct ifreq ifr; +- +- if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { +- wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) " +- "failed: %s", __func__, strerror(errno)); ++ for (i = 0; i < NUM_WEP_KEYS; i++) { ++ if (!hapd->conf->ssid.wep.key[i]) ++ continue; ++ wpa_printf(MSG_ERROR, ++ "VLAN: Refusing to set up VLAN iface %s with WEP", ++ vlan->ifname); + return -1; + } + +- os_memset(&ifr, 0, sizeof(ifr)); +- os_strlcpy(ifr.ifr_name, if_name, IFNAMSIZ); +- +- if (ioctl(fd, SIOCGIFFLAGS, &ifr) != 0) { +- wpa_printf(MSG_ERROR, "VLAN: %s: ioctl(SIOCGIFFLAGS) failed " +- "for interface %s: %s", +- __func__, if_name, strerror(errno)); +- close(fd); ++ if (!iface_exists(vlan->ifname)) ++ ret = hostapd_vlan_if_add(hapd, vlan->ifname); ++ else if (!existsok) + return -1; +- } +- +- if (up) +- ifr.ifr_flags |= IFF_UP; + else +- ifr.ifr_flags &= ~IFF_UP; ++ ret = 0; + +- if (ioctl(fd, SIOCSIFFLAGS, &ifr) != 0) { +- wpa_printf(MSG_ERROR, "VLAN: %s: ioctl(SIOCSIFFLAGS) failed " +- "for interface %s (up=%d): %s", +- __func__, if_name, up, strerror(errno)); +- close(fd); +- return -1; +- } ++ if (ret) ++ return ret; + +- close(fd); +- return 0; +-} ++ ifconfig_up(vlan->ifname); /* else wpa group will fail fatal */ + ++ if (hapd->wpa_auth) ++ ret = wpa_auth_ensure_group(hapd->wpa_auth, vlan->vlan_id); + +-static int ifconfig_up(const char *if_name) +-{ +- wpa_printf(MSG_DEBUG, "VLAN: Set interface %s up", if_name); +- return ifconfig_helper(if_name, 1); +-} ++ if (ret == 0) ++ return ret; + ++ wpa_printf(MSG_ERROR, "WPA initialization for VLAN %d failed (%d)", ++ vlan->vlan_id, ret); ++ if (wpa_auth_release_group(hapd->wpa_auth, vlan->vlan_id)) ++ wpa_printf(MSG_ERROR, "WPA deinit of %s failed", vlan->ifname); + +-static int ifconfig_down(const char *if_name) +-{ +- wpa_printf(MSG_DEBUG, "VLAN: Set interface %s down", if_name); +- return ifconfig_helper(if_name, 0); +-} ++ /* group state machine setup failed */ ++ if (hostapd_vlan_if_remove(hapd, vlan->ifname)) ++ wpa_printf(MSG_ERROR, "Removal of %s failed", vlan->ifname); + +- +-/* +- * These are only available in recent linux headers (without the leading +- * underscore). +- */ +-#define _GET_VLAN_REALDEV_NAME_CMD 8 +-#define _GET_VLAN_VID_CMD 9 +- +-/* This value should be 256 ONLY. If it is something else, then hostapd +- * might crash!, as this value has been hard-coded in 2.4.x kernel +- * bridging code. +- */ +-#define MAX_BR_PORTS 256 +- +-static int br_delif(const char *br_name, const char *if_name) +-{ +- int fd; +- struct ifreq ifr; +- unsigned long args[2]; +- int if_index; +- +- wpa_printf(MSG_DEBUG, "VLAN: br_delif(%s, %s)", br_name, if_name); +- if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { +- wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) " +- "failed: %s", __func__, strerror(errno)); +- return -1; +- } +- +- if_index = if_nametoindex(if_name); +- +- if (if_index == 0) { +- wpa_printf(MSG_ERROR, "VLAN: %s: Failure determining " +- "interface index for '%s'", +- __func__, if_name); +- close(fd); +- return -1; +- } +- +- args[0] = BRCTL_DEL_IF; +- args[1] = if_index; +- +- os_strlcpy(ifr.ifr_name, br_name, sizeof(ifr.ifr_name)); +- ifr.ifr_data = (__caddr_t) args; +- +- if (ioctl(fd, SIOCDEVPRIVATE, &ifr) < 0 && errno != EINVAL) { +- /* No error if interface already removed. */ +- wpa_printf(MSG_ERROR, "VLAN: %s: ioctl[SIOCDEVPRIVATE," +- "BRCTL_DEL_IF] failed for br_name=%s if_name=%s: " +- "%s", __func__, br_name, if_name, strerror(errno)); +- close(fd); +- return -1; +- } +- +- close(fd); +- return 0; ++ return ret; + } + + +-/* +- Add interface 'if_name' to the bridge 'br_name' +- +- returns -1 on error +- returns 1 if the interface is already part of the bridge +- returns 0 otherwise +-*/ +-static int br_addif(const char *br_name, const char *if_name) ++int vlan_if_remove(struct hostapd_data *hapd, struct hostapd_vlan *vlan) + { +- int fd; +- struct ifreq ifr; +- unsigned long args[2]; +- int if_index; ++ int ret; + +- wpa_printf(MSG_DEBUG, "VLAN: br_addif(%s, %s)", br_name, if_name); +- if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { +- wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) " +- "failed: %s", __func__, strerror(errno)); +- return -1; +- } ++ ret = wpa_auth_release_group(hapd->wpa_auth, vlan->vlan_id); ++ if (ret) ++ wpa_printf(MSG_ERROR, ++ "WPA deinitialization for VLAN %d failed (%d)", ++ vlan->vlan_id, ret); + +- if_index = if_nametoindex(if_name); +- +- if (if_index == 0) { +- wpa_printf(MSG_ERROR, "VLAN: %s: Failure determining " +- "interface index for '%s'", +- __func__, if_name); +- close(fd); +- return -1; +- } +- +- args[0] = BRCTL_ADD_IF; +- args[1] = if_index; +- +- os_strlcpy(ifr.ifr_name, br_name, sizeof(ifr.ifr_name)); +- ifr.ifr_data = (__caddr_t) args; +- +- if (ioctl(fd, SIOCDEVPRIVATE, &ifr) < 0) { +- if (errno == EBUSY) { +- /* The interface is already added. */ +- close(fd); +- return 1; +- } +- +- wpa_printf(MSG_ERROR, "VLAN: %s: ioctl[SIOCDEVPRIVATE," +- "BRCTL_ADD_IF] failed for br_name=%s if_name=%s: " +- "%s", __func__, br_name, if_name, strerror(errno)); +- close(fd); +- return -1; +- } +- +- close(fd); +- return 0; ++ return hostapd_vlan_if_remove(hapd, vlan->ifname); + } + + +-static int br_delbr(const char *br_name) +-{ +- int fd; +- unsigned long arg[2]; +- +- wpa_printf(MSG_DEBUG, "VLAN: br_delbr(%s)", br_name); +- if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { +- wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) " +- "failed: %s", __func__, strerror(errno)); +- return -1; +- } +- +- arg[0] = BRCTL_DEL_BRIDGE; +- arg[1] = (unsigned long) br_name; +- +- if (ioctl(fd, SIOCGIFBR, arg) < 0 && errno != ENXIO) { +- /* No error if bridge already removed. */ +- wpa_printf(MSG_ERROR, "VLAN: %s: BRCTL_DEL_BRIDGE failed for " +- "%s: %s", __func__, br_name, strerror(errno)); +- close(fd); +- return -1; +- } +- +- close(fd); +- return 0; +-} +- +- +-/* +- Add a bridge with the name 'br_name'. +- +- returns -1 on error +- returns 1 if the bridge already exists +- returns 0 otherwise +-*/ +-static int br_addbr(const char *br_name) +-{ +- int fd; +- unsigned long arg[4]; +- struct ifreq ifr; +- +- wpa_printf(MSG_DEBUG, "VLAN: br_addbr(%s)", br_name); +- if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { +- wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) " +- "failed: %s", __func__, strerror(errno)); +- return -1; +- } +- +- arg[0] = BRCTL_ADD_BRIDGE; +- arg[1] = (unsigned long) br_name; +- +- if (ioctl(fd, SIOCGIFBR, arg) < 0) { +- if (errno == EEXIST) { +- /* The bridge is already added. */ +- close(fd); +- return 1; +- } else { +- wpa_printf(MSG_ERROR, "VLAN: %s: BRCTL_ADD_BRIDGE " +- "failed for %s: %s", +- __func__, br_name, strerror(errno)); +- close(fd); +- return -1; +- } +- } +- +- /* Decrease forwarding delay to avoid EAPOL timeouts. */ +- os_memset(&ifr, 0, sizeof(ifr)); +- os_strlcpy(ifr.ifr_name, br_name, IFNAMSIZ); +- arg[0] = BRCTL_SET_BRIDGE_FORWARD_DELAY; +- arg[1] = 1; +- arg[2] = 0; +- arg[3] = 0; +- ifr.ifr_data = (char *) &arg; +- if (ioctl(fd, SIOCDEVPRIVATE, &ifr) < 0) { +- wpa_printf(MSG_ERROR, "VLAN: %s: " +- "BRCTL_SET_BRIDGE_FORWARD_DELAY (1 sec) failed for " +- "%s: %s", __func__, br_name, strerror(errno)); +- /* Continue anyway */ +- } +- +- close(fd); +- return 0; +-} +- +- +-static int br_getnumports(const char *br_name) +-{ +- int fd; +- int i; +- int port_cnt = 0; +- unsigned long arg[4]; +- int ifindices[MAX_BR_PORTS]; +- struct ifreq ifr; +- +- if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { +- wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) " +- "failed: %s", __func__, strerror(errno)); +- return -1; +- } +- +- arg[0] = BRCTL_GET_PORT_LIST; +- arg[1] = (unsigned long) ifindices; +- arg[2] = MAX_BR_PORTS; +- arg[3] = 0; +- +- os_memset(ifindices, 0, sizeof(ifindices)); +- os_strlcpy(ifr.ifr_name, br_name, sizeof(ifr.ifr_name)); +- ifr.ifr_data = (__caddr_t) arg; +- +- if (ioctl(fd, SIOCDEVPRIVATE, &ifr) < 0) { +- wpa_printf(MSG_ERROR, "VLAN: %s: BRCTL_GET_PORT_LIST " +- "failed for %s: %s", +- __func__, br_name, strerror(errno)); +- close(fd); +- return -1; +- } +- +- for (i = 1; i < MAX_BR_PORTS; i++) { +- if (ifindices[i] > 0) { +- port_cnt++; +- } +- } +- +- close(fd); +- return port_cnt; +-} +- +- +-#ifndef CONFIG_VLAN_NETLINK +- +-int vlan_rem(const char *if_name) +-{ +- int fd; +- struct vlan_ioctl_args if_request; +- +- wpa_printf(MSG_DEBUG, "VLAN: vlan_rem(%s)", if_name); +- if ((os_strlen(if_name) + 1) > sizeof(if_request.device1)) { +- wpa_printf(MSG_ERROR, "VLAN: Interface name too long: '%s'", +- if_name); +- return -1; +- } +- +- if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { +- wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) " +- "failed: %s", __func__, strerror(errno)); +- return -1; +- } +- +- os_memset(&if_request, 0, sizeof(if_request)); +- +- os_strlcpy(if_request.device1, if_name, sizeof(if_request.device1)); +- if_request.cmd = DEL_VLAN_CMD; +- +- if (ioctl(fd, SIOCSIFVLAN, &if_request) < 0) { +- wpa_printf(MSG_ERROR, "VLAN: %s: DEL_VLAN_CMD failed for %s: " +- "%s", __func__, if_name, strerror(errno)); +- close(fd); +- return -1; +- } +- +- close(fd); +- return 0; +-} +- +- +-/* +- Add a vlan interface with VLAN ID 'vid' and tagged interface +- 'if_name'. +- +- returns -1 on error +- returns 1 if the interface already exists +- returns 0 otherwise +-*/ +-int vlan_add(const char *if_name, int vid, const char *vlan_if_name) +-{ +- int fd; +- struct vlan_ioctl_args if_request; +- +- wpa_printf(MSG_DEBUG, "VLAN: vlan_add(if_name=%s, vid=%d)", +- if_name, vid); +- ifconfig_up(if_name); +- +- if ((os_strlen(if_name) + 1) > sizeof(if_request.device1)) { +- wpa_printf(MSG_ERROR, "VLAN: Interface name too long: '%s'", +- if_name); +- return -1; +- } +- +- if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { +- wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) " +- "failed: %s", __func__, strerror(errno)); +- return -1; +- } +- +- os_memset(&if_request, 0, sizeof(if_request)); +- +- /* Determine if a suitable vlan device already exists. */ +- +- os_snprintf(if_request.device1, sizeof(if_request.device1), "vlan%d", +- vid); +- +- if_request.cmd = _GET_VLAN_VID_CMD; +- +- if (ioctl(fd, SIOCSIFVLAN, &if_request) == 0) { +- +- if (if_request.u.VID == vid) { +- if_request.cmd = _GET_VLAN_REALDEV_NAME_CMD; +- +- if (ioctl(fd, SIOCSIFVLAN, &if_request) == 0 && +- os_strncmp(if_request.u.device2, if_name, +- sizeof(if_request.u.device2)) == 0) { +- close(fd); +- wpa_printf(MSG_DEBUG, "VLAN: vlan_add: " +- "if_name %s exists already", +- if_request.device1); +- return 1; +- } +- } +- } +- +- /* A suitable vlan device does not already exist, add one. */ +- +- os_memset(&if_request, 0, sizeof(if_request)); +- os_strlcpy(if_request.device1, if_name, sizeof(if_request.device1)); +- if_request.u.VID = vid; +- if_request.cmd = ADD_VLAN_CMD; +- +- if (ioctl(fd, SIOCSIFVLAN, &if_request) < 0) { +- wpa_printf(MSG_ERROR, "VLAN: %s: ADD_VLAN_CMD failed for %s: " +- "%s", +- __func__, if_request.device1, strerror(errno)); +- close(fd); +- return -1; +- } +- +- close(fd); +- return 0; +-} +- +- +-static int vlan_set_name_type(unsigned int name_type) +-{ +- int fd; +- struct vlan_ioctl_args if_request; +- +- wpa_printf(MSG_DEBUG, "VLAN: vlan_set_name_type(name_type=%u)", +- name_type); +- if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { +- wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) " +- "failed: %s", __func__, strerror(errno)); +- return -1; +- } +- +- os_memset(&if_request, 0, sizeof(if_request)); +- +- if_request.u.name_type = name_type; +- if_request.cmd = SET_VLAN_NAME_TYPE_CMD; +- if (ioctl(fd, SIOCSIFVLAN, &if_request) < 0) { +- wpa_printf(MSG_ERROR, "VLAN: %s: SET_VLAN_NAME_TYPE_CMD " +- "name_type=%u failed: %s", +- __func__, name_type, strerror(errno)); +- close(fd); +- return -1; +- } +- +- close(fd); +- return 0; +-} +- +-#endif /* CONFIG_VLAN_NETLINK */ +- +- +-static void vlan_newlink(char *ifname, struct hostapd_data *hapd) +-{ +- char vlan_ifname[IFNAMSIZ]; +- char br_name[IFNAMSIZ]; +- struct hostapd_vlan *vlan = hapd->conf->vlan; +- char *tagged_interface = hapd->conf->ssid.vlan_tagged_interface; +- int vlan_naming = hapd->conf->ssid.vlan_naming; +- int clean; +- +- wpa_printf(MSG_DEBUG, "VLAN: vlan_newlink(%s)", ifname); +- +- while (vlan) { +- if (os_strcmp(ifname, vlan->ifname) == 0 && !vlan->configured) { +- vlan->configured = 1; +- +- if (hapd->conf->vlan_bridge[0]) { +- os_snprintf(br_name, sizeof(br_name), "%s%d", +- hapd->conf->vlan_bridge, +- vlan->vlan_id); +- } else if (tagged_interface) { +- os_snprintf(br_name, sizeof(br_name), +- "br%s.%d", tagged_interface, +- vlan->vlan_id); +- } else { +- os_snprintf(br_name, sizeof(br_name), +- "brvlan%d", vlan->vlan_id); +- } +- +- dyn_iface_get(hapd, br_name, +- br_addbr(br_name) ? 0 : DVLAN_CLEAN_BR); +- +- ifconfig_up(br_name); +- +- if (tagged_interface) { +- if (vlan_naming == +- DYNAMIC_VLAN_NAMING_WITH_DEVICE) +- os_snprintf(vlan_ifname, +- sizeof(vlan_ifname), +- "%s.%d", tagged_interface, +- vlan->vlan_id); +- else +- os_snprintf(vlan_ifname, +- sizeof(vlan_ifname), +- "vlan%d", vlan->vlan_id); +- +- clean = 0; +- ifconfig_up(tagged_interface); +- if (!vlan_add(tagged_interface, vlan->vlan_id, +- vlan_ifname)) +- clean |= DVLAN_CLEAN_VLAN; +- +- if (!br_addif(br_name, vlan_ifname)) +- clean |= DVLAN_CLEAN_VLAN_PORT; +- +- dyn_iface_get(hapd, vlan_ifname, clean); +- +- ifconfig_up(vlan_ifname); +- } +- +- if (!br_addif(br_name, ifname)) +- vlan->clean |= DVLAN_CLEAN_WLAN_PORT; +- +- ifconfig_up(ifname); +- +- break; +- } +- vlan = vlan->next; +- } +-} +- +- +-static void vlan_dellink(char *ifname, struct hostapd_data *hapd) +-{ +- char vlan_ifname[IFNAMSIZ]; +- char br_name[IFNAMSIZ]; +- struct hostapd_vlan *first, *prev, *vlan = hapd->conf->vlan; +- char *tagged_interface = hapd->conf->ssid.vlan_tagged_interface; +- int vlan_naming = hapd->conf->ssid.vlan_naming; +- int clean; +- +- wpa_printf(MSG_DEBUG, "VLAN: vlan_dellink(%s)", ifname); +- +- first = prev = vlan; +- +- while (vlan) { +- if (os_strcmp(ifname, vlan->ifname) == 0 && +- vlan->configured) { +- if (hapd->conf->vlan_bridge[0]) { +- os_snprintf(br_name, sizeof(br_name), "%s%d", +- hapd->conf->vlan_bridge, +- vlan->vlan_id); +- } else if (tagged_interface) { +- os_snprintf(br_name, sizeof(br_name), +- "br%s.%d", tagged_interface, +- vlan->vlan_id); +- } else { +- os_snprintf(br_name, sizeof(br_name), +- "brvlan%d", vlan->vlan_id); +- } +- +- if (vlan->clean & DVLAN_CLEAN_WLAN_PORT) +- br_delif(br_name, vlan->ifname); +- +- if (tagged_interface) { +- if (vlan_naming == +- DYNAMIC_VLAN_NAMING_WITH_DEVICE) +- os_snprintf(vlan_ifname, +- sizeof(vlan_ifname), +- "%s.%d", tagged_interface, +- vlan->vlan_id); +- else +- os_snprintf(vlan_ifname, +- sizeof(vlan_ifname), +- "vlan%d", vlan->vlan_id); +- +- clean = dyn_iface_put(hapd, vlan_ifname); +- +- if (clean & DVLAN_CLEAN_VLAN_PORT) +- br_delif(br_name, vlan_ifname); +- +- if (clean & DVLAN_CLEAN_VLAN) { +- ifconfig_down(vlan_ifname); +- vlan_rem(vlan_ifname); +- } +- } +- +- clean = dyn_iface_put(hapd, br_name); +- if ((clean & DVLAN_CLEAN_BR) && +- br_getnumports(br_name) == 0) { +- ifconfig_down(br_name); +- br_delbr(br_name); +- } +- } +- +- if (os_strcmp(ifname, vlan->ifname) == 0) { +- if (vlan == first) { +- hapd->conf->vlan = vlan->next; +- } else { +- prev->next = vlan->next; +- } +- os_free(vlan); +- +- break; +- } +- prev = vlan; +- vlan = vlan->next; +- } +-} +- +- +-static void +-vlan_read_ifnames(struct nlmsghdr *h, size_t len, int del, +- struct hostapd_data *hapd) +-{ +- struct ifinfomsg *ifi; +- int attrlen, nlmsg_len, rta_len; +- struct rtattr *attr; +- char ifname[IFNAMSIZ + 1]; +- +- if (len < sizeof(*ifi)) +- return; +- +- ifi = NLMSG_DATA(h); +- +- nlmsg_len = NLMSG_ALIGN(sizeof(struct ifinfomsg)); +- +- attrlen = h->nlmsg_len - nlmsg_len; +- if (attrlen < 0) +- return; +- +- attr = (struct rtattr *) (((char *) ifi) + nlmsg_len); +- +- os_memset(ifname, 0, sizeof(ifname)); +- rta_len = RTA_ALIGN(sizeof(struct rtattr)); +- while (RTA_OK(attr, attrlen)) { +- if (attr->rta_type == IFLA_IFNAME) { +- int n = attr->rta_len - rta_len; +- if (n < 0) +- break; +- +- if ((size_t) n >= sizeof(ifname)) +- n = sizeof(ifname) - 1; +- os_memcpy(ifname, ((char *) attr) + rta_len, n); +- +- } +- +- attr = RTA_NEXT(attr, attrlen); +- } +- +- if (!ifname[0]) +- return; +- if (del && if_nametoindex(ifname)) { +- /* interface still exists, race condition -> +- * iface has just been recreated */ +- return; +- } +- +- wpa_printf(MSG_DEBUG, +- "VLAN: RTM_%sLINK: ifi_index=%d ifname=%s ifi_family=%d ifi_flags=0x%x (%s%s%s%s)", +- del ? "DEL" : "NEW", +- ifi->ifi_index, ifname, ifi->ifi_family, ifi->ifi_flags, +- (ifi->ifi_flags & IFF_UP) ? "[UP]" : "", +- (ifi->ifi_flags & IFF_RUNNING) ? "[RUNNING]" : "", +- (ifi->ifi_flags & IFF_LOWER_UP) ? "[LOWER_UP]" : "", +- (ifi->ifi_flags & IFF_DORMANT) ? "[DORMANT]" : ""); +- +- if (del) +- vlan_dellink(ifname, hapd); +- else +- vlan_newlink(ifname, hapd); +-} +- +- +-static void vlan_event_receive(int sock, void *eloop_ctx, void *sock_ctx) +-{ +- char buf[8192]; +- int left; +- struct sockaddr_nl from; +- socklen_t fromlen; +- struct nlmsghdr *h; +- struct hostapd_data *hapd = eloop_ctx; +- +- fromlen = sizeof(from); +- left = recvfrom(sock, buf, sizeof(buf), MSG_DONTWAIT, +- (struct sockaddr *) &from, &fromlen); +- if (left < 0) { +- if (errno != EINTR && errno != EAGAIN) +- wpa_printf(MSG_ERROR, "VLAN: %s: recvfrom failed: %s", +- __func__, strerror(errno)); +- return; +- } +- +- h = (struct nlmsghdr *) buf; +- while (NLMSG_OK(h, left)) { +- int len, plen; +- +- len = h->nlmsg_len; +- plen = len - sizeof(*h); +- if (len > left || plen < 0) { +- wpa_printf(MSG_DEBUG, "VLAN: Malformed netlink " +- "message: len=%d left=%d plen=%d", +- len, left, plen); +- break; +- } +- +- switch (h->nlmsg_type) { +- case RTM_NEWLINK: +- vlan_read_ifnames(h, plen, 0, hapd); +- break; +- case RTM_DELLINK: +- vlan_read_ifnames(h, plen, 1, hapd); +- break; +- } +- +- h = NLMSG_NEXT(h, left); +- } +- +- if (left > 0) { +- wpa_printf(MSG_DEBUG, "VLAN: %s: %d extra bytes in the end of " +- "netlink message", __func__, left); +- } +-} +- +- +-static struct full_dynamic_vlan * +-full_dynamic_vlan_init(struct hostapd_data *hapd) +-{ +- struct sockaddr_nl local; +- struct full_dynamic_vlan *priv; +- +- priv = os_zalloc(sizeof(*priv)); +- if (priv == NULL) +- return NULL; +- +-#ifndef CONFIG_VLAN_NETLINK +- vlan_set_name_type(hapd->conf->ssid.vlan_naming == +- DYNAMIC_VLAN_NAMING_WITH_DEVICE ? +- VLAN_NAME_TYPE_RAW_PLUS_VID_NO_PAD : +- VLAN_NAME_TYPE_PLUS_VID_NO_PAD); +-#endif /* CONFIG_VLAN_NETLINK */ +- +- priv->s = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE); +- if (priv->s < 0) { +- wpa_printf(MSG_ERROR, "VLAN: %s: socket(PF_NETLINK,SOCK_RAW," +- "NETLINK_ROUTE) failed: %s", +- __func__, strerror(errno)); +- os_free(priv); +- return NULL; +- } +- +- os_memset(&local, 0, sizeof(local)); +- local.nl_family = AF_NETLINK; +- local.nl_groups = RTMGRP_LINK; +- if (bind(priv->s, (struct sockaddr *) &local, sizeof(local)) < 0) { +- wpa_printf(MSG_ERROR, "VLAN: %s: bind(netlink) failed: %s", +- __func__, strerror(errno)); +- close(priv->s); +- os_free(priv); +- return NULL; +- } +- +- if (eloop_register_read_sock(priv->s, vlan_event_receive, hapd, NULL)) +- { +- close(priv->s); +- os_free(priv); +- return NULL; +- } +- +- return priv; +-} +- +- +-static void full_dynamic_vlan_deinit(struct full_dynamic_vlan *priv) +-{ +- if (priv == NULL) +- return; +- eloop_unregister_read_sock(priv->s); +- close(priv->s); +- os_free(priv); +-} +-#endif /* CONFIG_FULL_DYNAMIC_VLAN */ +- +- +-int vlan_setup_encryption_dyn(struct hostapd_data *hapd, const char *dyn_vlan) +-{ +- int i; +- +- if (dyn_vlan == NULL) +- return 0; +- +- /* Static WEP keys are set here; IEEE 802.1X and WPA uses their own +- * functions for setting up dynamic broadcast keys. */ +- for (i = 0; i < 4; i++) { +- if (hapd->conf->ssid.wep.key[i] && +- hostapd_drv_set_key(dyn_vlan, hapd, WPA_ALG_WEP, NULL, i, +- i == hapd->conf->ssid.wep.idx, NULL, 0, +- hapd->conf->ssid.wep.key[i], +- hapd->conf->ssid.wep.len[i])) +- { +- wpa_printf(MSG_ERROR, "VLAN: Could not set WEP " +- "encryption for dynamic VLAN"); +- return -1; +- } +- } +- +- return 0; +-} +- +- + static int vlan_dynamic_add(struct hostapd_data *hapd, + struct hostapd_vlan *vlan) + { + while (vlan) { + if (vlan->vlan_id != VLAN_ID_WILDCARD) { +- if (hostapd_vlan_if_add(hapd, vlan->ifname)) { +- if (errno != EEXIST) { +- wpa_printf(MSG_ERROR, "VLAN: Could " +- "not add VLAN %s: %s", +- vlan->ifname, +- strerror(errno)); +- return -1; +- } ++ if (vlan_if_add(hapd, vlan, 1)) { ++ wpa_printf(MSG_ERROR, ++ "VLAN: Could not add VLAN %s: %s", ++ vlan->ifname, strerror(errno)); ++ return -1; + } + #ifdef CONFIG_FULL_DYNAMIC_VLAN +- ifconfig_up(vlan->ifname); ++ vlan_newlink(vlan->ifname, hapd); + #endif /* CONFIG_FULL_DYNAMIC_VLAN */ + } + +@@ -942,15 +109,17 @@ + while (vlan) { + next = vlan->next; + ++#ifdef CONFIG_FULL_DYNAMIC_VLAN ++ /* vlan_dellink() takes care of cleanup and interface removal */ ++ if (vlan->vlan_id != VLAN_ID_WILDCARD) ++ vlan_dellink(vlan->ifname, hapd); ++#else /* CONFIG_FULL_DYNAMIC_VLAN */ + if (vlan->vlan_id != VLAN_ID_WILDCARD && +- hostapd_vlan_if_remove(hapd, vlan->ifname)) { ++ vlan_if_remove(hapd, vlan)) { + wpa_printf(MSG_ERROR, "VLAN: Could not remove VLAN " + "iface: %s: %s", + vlan->ifname, strerror(errno)); + } +-#ifdef CONFIG_FULL_DYNAMIC_VLAN +- if (vlan->clean) +- vlan_dellink(vlan->ifname, hapd); + #endif /* CONFIG_FULL_DYNAMIC_VLAN */ + + vlan = next; +@@ -964,10 +133,13 @@ + hapd->full_dynamic_vlan = full_dynamic_vlan_init(hapd); + #endif /* CONFIG_FULL_DYNAMIC_VLAN */ + +- if (hapd->conf->ssid.dynamic_vlan != DYNAMIC_VLAN_DISABLED && ++ if ((hapd->conf->ssid.dynamic_vlan != DYNAMIC_VLAN_DISABLED || ++ hapd->conf->ssid.per_sta_vif) && + !hapd->conf->vlan) { + /* dynamic vlans enabled but no (or empty) vlan_file given */ + struct hostapd_vlan *vlan; ++ int ret; ++ + vlan = os_zalloc(sizeof(*vlan)); + if (vlan == NULL) { + wpa_printf(MSG_ERROR, "Out of memory while assigning " +@@ -976,8 +148,16 @@ + } + + vlan->vlan_id = VLAN_ID_WILDCARD; +- os_snprintf(vlan->ifname, sizeof(vlan->ifname), "%s.#", +- hapd->conf->iface); ++ ret = os_snprintf(vlan->ifname, sizeof(vlan->ifname), "%s.#", ++ hapd->conf->iface); ++ if (ret >= (int) sizeof(vlan->ifname)) { ++ wpa_printf(MSG_WARNING, ++ "VLAN: Interface name was truncated to %s", ++ vlan->ifname); ++ } else if (ret < 0) { ++ os_free(vlan); ++ return ret; ++ } + vlan->next = hapd->conf->vlan; + hapd->conf->vlan = vlan; + } +@@ -1002,50 +182,51 @@ + + struct hostapd_vlan * vlan_add_dynamic(struct hostapd_data *hapd, + struct hostapd_vlan *vlan, +- int vlan_id) ++ int vlan_id, ++ struct vlan_description *vlan_desc) + { +- struct hostapd_vlan *n = NULL; +- char *ifname, *pos; ++ struct hostapd_vlan *n; ++ char ifname[IFNAMSIZ + 1], *pos; ++ int ret; + +- if (vlan == NULL || vlan_id <= 0 || vlan_id > MAX_VLAN_ID || +- vlan->vlan_id != VLAN_ID_WILDCARD) ++ if (vlan == NULL || vlan->vlan_id != VLAN_ID_WILDCARD) + return NULL; + + wpa_printf(MSG_DEBUG, "VLAN: %s(vlan_id=%d ifname=%s)", + __func__, vlan_id, vlan->ifname); +- ifname = os_strdup(vlan->ifname); +- if (ifname == NULL) +- return NULL; ++ os_strlcpy(ifname, vlan->ifname, sizeof(ifname)); + pos = os_strchr(ifname, '#'); + if (pos == NULL) +- goto free_ifname; ++ return NULL; + *pos++ = '\0'; + + n = os_zalloc(sizeof(*n)); + if (n == NULL) +- goto free_ifname; ++ return NULL; + + n->vlan_id = vlan_id; ++ if (vlan_desc) ++ n->vlan_desc = *vlan_desc; + n->dynamic_vlan = 1; + +- os_snprintf(n->ifname, sizeof(n->ifname), "%s%d%s", ifname, vlan_id, +- pos); +- +- if (hostapd_vlan_if_add(hapd, n->ifname)) { ++ ret = os_snprintf(n->ifname, sizeof(n->ifname), "%s%d%s", ++ ifname, vlan_id, pos); ++ if (os_snprintf_error(sizeof(n->ifname), ret)) { + os_free(n); +- n = NULL; +- goto free_ifname; ++ return NULL; + } ++ os_strlcpy(n->bridge, vlan->bridge, sizeof(n->bridge)); + + n->next = hapd->conf->vlan; + hapd->conf->vlan = n; + +-#ifdef CONFIG_FULL_DYNAMIC_VLAN +- ifconfig_up(n->ifname); +-#endif /* CONFIG_FULL_DYNAMIC_VLAN */ ++ /* hapd->conf->vlan needs this new VLAN here for WPA setup */ ++ if (vlan_if_add(hapd, n, 0)) { ++ hapd->conf->vlan = n->next; ++ os_free(n); ++ n = NULL; ++ } + +-free_ifname: +- os_free(ifname); + return n; + } + +@@ -1054,7 +235,7 @@ + { + struct hostapd_vlan *vlan; + +- if (vlan_id <= 0 || vlan_id > MAX_VLAN_ID) ++ if (vlan_id <= 0) + return 1; + + wpa_printf(MSG_DEBUG, "VLAN: %s(ifname=%s vlan_id=%d)", +@@ -1073,7 +254,7 @@ + return 1; + + if (vlan->dynamic_vlan == 0) { +- hostapd_vlan_if_remove(hapd, vlan->ifname); ++ vlan_if_remove(hapd, vlan); + #ifdef CONFIG_FULL_DYNAMIC_VLAN + vlan_dellink(vlan->ifname, hapd); + #endif /* CONFIG_FULL_DYNAMIC_VLAN */ +--- contrib/wpa/src/ap/vlan_init.h.orig ++++ contrib/wpa/src/ap/vlan_init.h +@@ -15,10 +15,9 @@ + void vlan_deinit(struct hostapd_data *hapd); + struct hostapd_vlan * vlan_add_dynamic(struct hostapd_data *hapd, + struct hostapd_vlan *vlan, +- int vlan_id); ++ int vlan_id, ++ struct vlan_description *vlan_desc); + int vlan_remove_dynamic(struct hostapd_data *hapd, int vlan_id); +-int vlan_setup_encryption_dyn(struct hostapd_data *hapd, +- const char *dyn_vlan); + #else /* CONFIG_NO_VLAN */ + static inline int vlan_init(struct hostapd_data *hapd) + { +@@ -29,9 +28,9 @@ + { + } + +-static inline struct hostapd_vlan * vlan_add_dynamic(struct hostapd_data *hapd, +- struct hostapd_vlan *vlan, +- int vlan_id) ++static inline struct hostapd_vlan * ++vlan_add_dynamic(struct hostapd_data *hapd, struct hostapd_vlan *vlan, ++ int vlan_id, struct vlan_description *vlan_desc) + { + return NULL; + } +@@ -40,12 +39,6 @@ + { + return -1; + } +- +-static inline int vlan_setup_encryption_dyn(struct hostapd_data *hapd, +- const char *dyn_vlan) +-{ +- return -1; +-} + #endif /* CONFIG_NO_VLAN */ + + #endif /* VLAN_INIT_H */ +--- contrib/wpa/src/ap/vlan_ioctl.c.orig ++++ contrib/wpa/src/ap/vlan_ioctl.c +@@ -0,0 +1,155 @@ ++/* ++ * hostapd / VLAN ioctl API ++ * Copyright 2003, Instant802 Networks, Inc. ++ * Copyright 2005-2006, Devicescape Software, Inc. ++ * Copyright (c) 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 ++ ++#include "utils/common.h" ++#include "common/linux_vlan.h" ++#include "vlan_util.h" ++ ++ ++int vlan_rem(const char *if_name) ++{ ++ int fd; ++ struct vlan_ioctl_args if_request; ++ ++ wpa_printf(MSG_DEBUG, "VLAN: vlan_rem(%s)", if_name); ++ if ((os_strlen(if_name) + 1) > sizeof(if_request.device1)) { ++ wpa_printf(MSG_ERROR, "VLAN: Interface name too long: '%s'", ++ if_name); ++ return -1; ++ } ++ ++ if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { ++ wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) " ++ "failed: %s", __func__, strerror(errno)); ++ return -1; ++ } ++ ++ os_memset(&if_request, 0, sizeof(if_request)); ++ ++ os_strlcpy(if_request.device1, if_name, sizeof(if_request.device1)); ++ if_request.cmd = DEL_VLAN_CMD; ++ ++ if (ioctl(fd, SIOCSIFVLAN, &if_request) < 0) { ++ wpa_printf(MSG_ERROR, "VLAN: %s: DEL_VLAN_CMD failed for %s: " ++ "%s", __func__, if_name, strerror(errno)); ++ close(fd); ++ return -1; ++ } ++ ++ close(fd); ++ return 0; ++} ++ ++ ++/* ++ Add a vlan interface with VLAN ID 'vid' and tagged interface ++ 'if_name'. ++ ++ returns -1 on error ++ returns 1 if the interface already exists ++ returns 0 otherwise ++*/ ++int vlan_add(const char *if_name, int vid, const char *vlan_if_name) ++{ ++ int fd; ++ struct vlan_ioctl_args if_request; ++ ++ wpa_printf(MSG_DEBUG, "VLAN: vlan_add(if_name=%s, vid=%d)", ++ if_name, vid); ++ ifconfig_up(if_name); ++ ++ if ((os_strlen(if_name) + 1) > sizeof(if_request.device1)) { ++ wpa_printf(MSG_ERROR, "VLAN: Interface name too long: '%s'", ++ if_name); ++ return -1; ++ } ++ ++ if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { ++ wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) " ++ "failed: %s", __func__, strerror(errno)); ++ return -1; ++ } ++ ++ os_memset(&if_request, 0, sizeof(if_request)); ++ ++ /* Determine if a suitable vlan device already exists. */ ++ ++ os_snprintf(if_request.device1, sizeof(if_request.device1), "vlan%d", ++ vid); ++ ++ if_request.cmd = GET_VLAN_VID_CMD; ++ ++ if (ioctl(fd, SIOCSIFVLAN, &if_request) == 0 && ++ if_request.u.VID == vid) { ++ if_request.cmd = GET_VLAN_REALDEV_NAME_CMD; ++ ++ if (ioctl(fd, SIOCSIFVLAN, &if_request) == 0 && ++ os_strncmp(if_request.u.device2, if_name, ++ sizeof(if_request.u.device2)) == 0) { ++ close(fd); ++ wpa_printf(MSG_DEBUG, ++ "VLAN: vlan_add: if_name %s exists already", ++ if_request.device1); ++ return 1; ++ } ++ } ++ ++ /* A suitable vlan device does not already exist, add one. */ ++ ++ os_memset(&if_request, 0, sizeof(if_request)); ++ os_strlcpy(if_request.device1, if_name, sizeof(if_request.device1)); ++ if_request.u.VID = vid; ++ if_request.cmd = ADD_VLAN_CMD; ++ ++ if (ioctl(fd, SIOCSIFVLAN, &if_request) < 0) { ++ wpa_printf(MSG_ERROR, ++ "VLAN: %s: ADD_VLAN_CMD failed for %s: %s", ++ __func__, if_request.device1, strerror(errno)); ++ close(fd); ++ return -1; ++ } ++ ++ close(fd); ++ return 0; ++} ++ ++ ++int vlan_set_name_type(unsigned int name_type) ++{ ++ int fd; ++ struct vlan_ioctl_args if_request; ++ ++ wpa_printf(MSG_DEBUG, "VLAN: vlan_set_name_type(name_type=%u)", ++ name_type); ++ if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { ++ wpa_printf(MSG_ERROR, ++ "VLAN: %s: socket(AF_INET,SOCK_STREAM) failed: %s", ++ __func__, strerror(errno)); ++ return -1; ++ } ++ ++ os_memset(&if_request, 0, sizeof(if_request)); ++ ++ if_request.u.name_type = name_type; ++ if_request.cmd = SET_VLAN_NAME_TYPE_CMD; ++ if (ioctl(fd, SIOCSIFVLAN, &if_request) < 0) { ++ wpa_printf(MSG_ERROR, ++ "VLAN: %s: SET_VLAN_NAME_TYPE_CMD name_type=%u failed: %s", ++ __func__, name_type, strerror(errno)); ++ close(fd); ++ return -1; ++ } ++ ++ close(fd); ++ return 0; ++} +--- contrib/wpa/src/ap/vlan_util.c.orig ++++ contrib/wpa/src/ap/vlan_util.c +@@ -7,18 +7,10 @@ + */ + + #include "utils/includes.h" +-#include +-#include +-#include +-#include +-#include +-#include + #include + #include + + #include "utils/common.h" +-#include "utils/eloop.h" +-#include "hostapd.h" + #include "vlan_util.h" + + /* +@@ -33,7 +25,6 @@ + { + int err, ret = -1; + struct nl_sock *handle = NULL; +- struct nl_cache *cache = NULL; + struct rtnl_link *rlink = NULL; + int if_idx = 0; + +@@ -65,22 +56,19 @@ + goto vlan_add_error; + } + +- err = rtnl_link_alloc_cache(handle, AF_UNSPEC, &cache); ++ err = rtnl_link_get_kernel(handle, 0, if_name, &rlink); + if (err < 0) { +- cache = NULL; +- wpa_printf(MSG_ERROR, "VLAN: failed to alloc cache: %s", +- nl_geterror(err)); +- goto vlan_add_error; +- } +- +- if (!(if_idx = rtnl_link_name2i(cache, if_name))) { + /* link does not exist */ + wpa_printf(MSG_ERROR, "VLAN: interface %s does not exist", + if_name); + goto vlan_add_error; + } ++ if_idx = rtnl_link_get_ifindex(rlink); ++ rtnl_link_put(rlink); ++ rlink = NULL; + +- if ((rlink = rtnl_link_get_by_name(cache, vlan_if_name))) { ++ err = rtnl_link_get_kernel(handle, 0, vlan_if_name, &rlink); ++ if (err >= 0) { + /* link does exist */ + rtnl_link_put(rlink); + rlink = NULL; +@@ -127,8 +115,6 @@ + vlan_add_error: + if (rlink) + rtnl_link_put(rlink); +- if (cache) +- nl_cache_free(cache); + if (handle) + nl_socket_free(handle); + return ret; +@@ -139,7 +125,6 @@ + { + int err, ret = -1; + struct nl_sock *handle = NULL; +- struct nl_cache *cache = NULL; + struct rtnl_link *rlink = NULL; + + wpa_printf(MSG_DEBUG, "VLAN: vlan_rem(if_name=%s)", if_name); +@@ -157,15 +142,8 @@ + goto vlan_rem_error; + } + +- err = rtnl_link_alloc_cache(handle, AF_UNSPEC, &cache); ++ err = rtnl_link_get_kernel(handle, 0, if_name, &rlink); + if (err < 0) { +- cache = NULL; +- wpa_printf(MSG_ERROR, "VLAN: failed to alloc cache: %s", +- nl_geterror(err)); +- goto vlan_rem_error; +- } +- +- if (!(rlink = rtnl_link_get_by_name(cache, if_name))) { + /* link does not exist */ + wpa_printf(MSG_ERROR, "VLAN: interface %s does not exists", + if_name); +@@ -184,9 +162,13 @@ + vlan_rem_error: + if (rlink) + rtnl_link_put(rlink); +- if (cache) +- nl_cache_free(cache); + if (handle) + nl_socket_free(handle); + return ret; + } ++ ++ ++int vlan_set_name_type(unsigned int name_type) ++{ ++ return 0; ++} +--- contrib/wpa/src/ap/vlan_util.h.orig ++++ contrib/wpa/src/ap/vlan_util.h +@@ -1,5 +1,5 @@ + /* +- * hostapd / VLAN netlink api ++ * hostapd / VLAN netlink/ioctl api + * Copyright (c) 2012, Michael Braun + * + * This software may be distributed under the terms of the BSD license. +@@ -9,7 +9,23 @@ + #ifndef VLAN_UTIL_H + #define VLAN_UTIL_H + ++struct hostapd_data; ++struct hostapd_vlan; ++struct full_dynamic_vlan; ++ + int vlan_add(const char *if_name, int vid, const char *vlan_if_name); + int vlan_rem(const char *if_name); ++int vlan_set_name_type(unsigned int name_type); + ++int ifconfig_helper(const char *if_name, int up); ++int ifconfig_up(const char *if_name); ++int iface_exists(const char *ifname); ++int vlan_if_remove(struct hostapd_data *hapd, struct hostapd_vlan *vlan); ++ ++struct full_dynamic_vlan * ++full_dynamic_vlan_init(struct hostapd_data *hapd); ++void full_dynamic_vlan_deinit(struct full_dynamic_vlan *priv); ++void vlan_newlink(const char *ifname, struct hostapd_data *hapd); ++void vlan_dellink(const char *ifname, struct hostapd_data *hapd); ++ + #endif /* VLAN_UTIL_H */ +--- contrib/wpa/src/ap/wmm.c.orig ++++ contrib/wpa/src/ap/wmm.c +@@ -21,11 +21,6 @@ + #include "wmm.h" + + +-/* TODO: maintain separate sequence and fragment numbers for each AC +- * TODO: IGMP snooping to track which multicasts to forward - and use QOS-DATA +- * if only WMM stations are receiving a certain group */ +- +- + static inline u8 wmm_aci_aifsn(int aifsn, int acm, int aci) + { + u8 ret; +@@ -157,8 +152,9 @@ + + int wmm_process_tspec(struct wmm_tspec_element *tspec) + { +- int medium_time, pps, duration; +- int up, psb, dir, tid; ++ u64 medium_time; ++ unsigned int pps, duration; ++ unsigned int up, psb, dir, tid; + u16 val, surplus; + + up = (tspec->ts_info[1] >> 3) & 0x07; +@@ -206,8 +202,9 @@ + return WMM_ADDTS_STATUS_INVALID_PARAMETERS; + } + +- medium_time = surplus * pps * duration / 0x2000; +- wpa_printf(MSG_DEBUG, "WMM: Estimated medium time: %u", medium_time); ++ medium_time = (u64) surplus * pps * duration / 0x2000; ++ wpa_printf(MSG_DEBUG, "WMM: Estimated medium time: %lu", ++ (unsigned long) medium_time); + + /* + * TODO: store list of granted (and still active) TSPECs and check +--- contrib/wpa/src/ap/wnm_ap.c.orig ++++ contrib/wpa/src/ap/wnm_ap.c +@@ -12,11 +12,13 @@ + #include "utils/eloop.h" + #include "common/ieee802_11_defs.h" + #include "common/wpa_ctrl.h" ++#include "common/ocv.h" + #include "ap/hostapd.h" + #include "ap/sta_info.h" + #include "ap/ap_config.h" + #include "ap/ap_drv_ops.h" + #include "ap/wpa_auth.h" ++#include "mbo_ap.h" + #include "wnm_ap.h" + + #define MAX_TFS_IE_LEN 1024 +@@ -53,8 +55,8 @@ + size_t gtk_elem_len = 0; + size_t igtk_elem_len = 0; + struct wnm_sleep_element wnmsleep_ie; +- u8 *wnmtfs_ie; +- u8 wnmsleep_ie_len; ++ u8 *wnmtfs_ie, *oci_ie; ++ u8 wnmsleep_ie_len, oci_ie_len; + u16 wnmtfs_ie_len; + u8 *pos; + struct sta_info *sta; +@@ -87,14 +89,47 @@ + wnmtfs_ie = NULL; + } + ++ oci_ie = NULL; ++ oci_ie_len = 0; ++#ifdef CONFIG_OCV ++ if (action_type == WNM_SLEEP_MODE_EXIT && ++ wpa_auth_uses_ocv(sta->wpa_sm)) { ++ struct wpa_channel_info ci; ++ ++ if (hostapd_drv_channel_info(hapd, &ci) != 0) { ++ wpa_printf(MSG_WARNING, ++ "Failed to get channel info for OCI element in WNM-Sleep Mode frame"); ++ os_free(wnmtfs_ie); ++ return -1; ++ } ++ ++ oci_ie_len = OCV_OCI_EXTENDED_LEN; ++ oci_ie = os_zalloc(oci_ie_len); ++ if (!oci_ie) { ++ wpa_printf(MSG_WARNING, ++ "Failed to allocate buffer for OCI element in WNM-Sleep Mode frame"); ++ os_free(wnmtfs_ie); ++ return -1; ++ } ++ ++ if (ocv_insert_extended_oci(&ci, oci_ie) < 0) { ++ os_free(wnmtfs_ie); ++ os_free(oci_ie); ++ return -1; ++ } ++ } ++#endif /* CONFIG_OCV */ ++ + #define MAX_GTK_SUBELEM_LEN 45 + #define MAX_IGTK_SUBELEM_LEN 26 + mgmt = os_zalloc(sizeof(*mgmt) + wnmsleep_ie_len + +- MAX_GTK_SUBELEM_LEN + MAX_IGTK_SUBELEM_LEN); ++ MAX_GTK_SUBELEM_LEN + MAX_IGTK_SUBELEM_LEN + ++ oci_ie_len); + if (mgmt == NULL) { + wpa_printf(MSG_DEBUG, "MLME: Failed to allocate buffer for " + "WNM-Sleep Response action frame"); +- return -1; ++ res = -1; ++ goto fail; + } + os_memcpy(mgmt->da, addr, ETH_ALEN); + os_memcpy(mgmt->sa, hapd->own_addr, ETH_ALEN); +@@ -107,6 +142,7 @@ + pos = (u8 *)mgmt->u.action.u.wnm_sleep_resp.variable; + /* add key data if MFP is enabled */ + if (!wpa_auth_uses_mfp(sta->wpa_sm) || ++ hapd->conf->wnm_sleep_mode_no_keys || + action_type != WNM_SLEEP_MODE_EXIT) { + mgmt->u.action.u.wnm_sleep_resp.keydata_len = 0; + } else { +@@ -116,11 +152,8 @@ + (int) gtk_elem_len); + #ifdef CONFIG_IEEE80211W + res = wpa_wnmsleep_igtk_subelem(sta->wpa_sm, pos); +- if (res < 0) { +- os_free(wnmtfs_ie); +- os_free(mgmt); +- return -1; +- } ++ if (res < 0) ++ goto fail; + igtk_elem_len = res; + pos += igtk_elem_len; + wpa_printf(MSG_DEBUG, "Pass 4 igtk_len = %d", +@@ -134,11 +167,18 @@ + os_memcpy(pos, &wnmsleep_ie, wnmsleep_ie_len); + /* copy TFS IE here */ + pos += wnmsleep_ie_len; +- if (wnmtfs_ie) ++ if (wnmtfs_ie) { + os_memcpy(pos, wnmtfs_ie, wnmtfs_ie_len); ++ pos += wnmtfs_ie_len; ++ } ++#ifdef CONFIG_OCV ++ /* copy OCV OCI here */ ++ if (oci_ie_len > 0) ++ os_memcpy(pos, oci_ie, oci_ie_len); ++#endif /* CONFIG_OCV */ + + len = 1 + sizeof(mgmt->u.action.u.wnm_sleep_resp) + gtk_elem_len + +- igtk_elem_len + wnmsleep_ie_len + wnmtfs_ie_len; ++ igtk_elem_len + wnmsleep_ie_len + wnmtfs_ie_len + oci_ie_len; + + /* In driver, response frame should be forced to sent when STA is in + * PS mode */ +@@ -174,7 +214,8 @@ + wpa_set_wnmsleep(sta->wpa_sm, 0); + hostapd_drv_wnm_oper(hapd, WNM_SLEEP_EXIT_CONFIRM, + addr, NULL, NULL); +- if (!wpa_auth_uses_mfp(sta->wpa_sm)) ++ if (!wpa_auth_uses_mfp(sta->wpa_sm) || ++ hapd->conf->wnm_sleep_mode_no_keys) + wpa_wnmsleep_rekey_gtk(sta->wpa_sm); + } + } else +@@ -182,7 +223,9 @@ + + #undef MAX_GTK_SUBELEM_LEN + #undef MAX_IGTK_SUBELEM_LEN ++fail: + os_free(wnmtfs_ie); ++ os_free(oci_ie); + os_free(mgmt); + return res; + } +@@ -199,18 +242,44 @@ + u8 *tfsreq_ie_start = NULL; + u8 *tfsreq_ie_end = NULL; + u16 tfsreq_ie_len = 0; ++#ifdef CONFIG_OCV ++ struct sta_info *sta; ++ const u8 *oci_ie = NULL; ++ u8 oci_ie_len = 0; ++#endif /* CONFIG_OCV */ + ++ if (!hapd->conf->wnm_sleep_mode) { ++ wpa_printf(MSG_DEBUG, "Ignore WNM-Sleep Mode Request from " ++ MACSTR " since WNM-Sleep Mode is disabled", ++ MAC2STR(addr)); ++ return; ++ } ++ ++ if (len < 1) { ++ wpa_printf(MSG_DEBUG, ++ "WNM: Ignore too short WNM-Sleep Mode Request from " ++ MACSTR, MAC2STR(addr)); ++ return; ++ } ++ + dialog_token = *pos++; + while (pos + 1 < frm + len) { + u8 ie_len = pos[1]; + if (pos + 2 + ie_len > frm + len) + break; +- if (*pos == WLAN_EID_WNMSLEEP) ++ if (*pos == WLAN_EID_WNMSLEEP && ++ ie_len >= (int) sizeof(*wnmsleep_ie) - 2) + wnmsleep_ie = (struct wnm_sleep_element *) pos; + else if (*pos == WLAN_EID_TFS_REQ) { + if (!tfsreq_ie_start) + tfsreq_ie_start = (u8 *) pos; + tfsreq_ie_end = (u8 *) pos; ++#ifdef CONFIG_OCV ++ } else if (*pos == WLAN_EID_EXTENSION && ie_len >= 1 && ++ pos[2] == WLAN_EID_EXT_OCV_OCI) { ++ oci_ie = pos + 3; ++ oci_ie_len = ie_len - 1; ++#endif /* CONFIG_OCV */ + } else + wpa_printf(MSG_DEBUG, "WNM: EID %d not recognized", + *pos); +@@ -222,6 +291,27 @@ + return; + } + ++#ifdef CONFIG_OCV ++ sta = ap_get_sta(hapd, addr); ++ if (wnmsleep_ie->action_type == WNM_SLEEP_MODE_EXIT && ++ sta && wpa_auth_uses_ocv(sta->wpa_sm)) { ++ struct wpa_channel_info ci; ++ ++ if (hostapd_drv_channel_info(hapd, &ci) != 0) { ++ wpa_printf(MSG_WARNING, ++ "Failed to get channel info to validate received OCI in WNM-Sleep Mode frame"); ++ return; ++ } ++ ++ if (ocv_verify_tx_params(oci_ie, oci_ie_len, &ci, ++ channel_width_to_int(ci.chanwidth), ++ ci.seg1_idx) != 0) { ++ wpa_msg(hapd, MSG_WARNING, "WNM: %s", ocv_errorstr); ++ return; ++ } ++ } ++#endif /* CONFIG_OCV */ ++ + if (wnmsleep_ie->action_type == WNM_SLEEP_MODE_ENTER && + tfsreq_ie_start && tfsreq_ie_end && + tfsreq_ie_end - tfsreq_ie_start >= 0) { +@@ -249,20 +339,14 @@ + + static int ieee802_11_send_bss_trans_mgmt_request(struct hostapd_data *hapd, + const u8 *addr, +- u8 dialog_token, +- const char *url) ++ u8 dialog_token) + { + struct ieee80211_mgmt *mgmt; +- size_t url_len, len; ++ size_t len; + u8 *pos; + int res; + +- if (url) +- url_len = os_strlen(url); +- else +- url_len = 0; +- +- mgmt = os_zalloc(sizeof(*mgmt) + (url_len ? 1 + url_len : 0)); ++ mgmt = os_zalloc(sizeof(*mgmt)); + if (mgmt == NULL) + return -1; + os_memcpy(mgmt->da, addr, ETH_ALEN); +@@ -277,11 +361,6 @@ + mgmt->u.action.u.bss_tm_req.disassoc_timer = host_to_le16(0); + mgmt->u.action.u.bss_tm_req.validity_interval = 1; + pos = mgmt->u.action.u.bss_tm_req.variable; +- if (url) { +- *pos++ += url_len; +- os_memcpy(pos, url, url_len); +- pos += url_len; +- } + + wpa_printf(MSG_DEBUG, "WNM: Send BSS Transition Management Request to " + MACSTR " dialog_token=%u req_mode=0x%x disassoc_timer=%u " +@@ -305,7 +384,21 @@ + { + u8 dialog_token, reason; + const u8 *pos, *end; ++ int enabled = hapd->conf->bss_transition; + ++#ifdef CONFIG_MBO ++ if (hapd->conf->mbo_enabled) ++ enabled = 1; ++#endif /* CONFIG_MBO */ ++ if (!enabled) { ++ wpa_printf(MSG_DEBUG, ++ "Ignore BSS Transition Management Query from " ++ MACSTR ++ " since BSS Transition Management is disabled", ++ MAC2STR(addr)); ++ return; ++ } ++ + if (len < 2) { + wpa_printf(MSG_DEBUG, "WNM: Ignore too short BSS Transition Management Query from " + MACSTR, MAC2STR(addr)); +@@ -324,10 +417,23 @@ + wpa_hexdump(MSG_DEBUG, "WNM: BSS Transition Candidate List Entries", + pos, end - pos); + +- ieee802_11_send_bss_trans_mgmt_request(hapd, addr, dialog_token, NULL); ++ ieee802_11_send_bss_trans_mgmt_request(hapd, addr, dialog_token); + } + + ++void ap_sta_reset_steer_flag_timer(void *eloop_ctx, void *timeout_ctx) ++{ ++ struct hostapd_data *hapd = eloop_ctx; ++ struct sta_info *sta = timeout_ctx; ++ ++ if (sta->agreed_to_steer) { ++ wpa_printf(MSG_DEBUG, "%s: Reset steering flag for STA " MACSTR, ++ hapd->conf->iface, MAC2STR(sta->addr)); ++ sta->agreed_to_steer = 0; ++ } ++} ++ ++ + static void ieee802_11_rx_bss_trans_mgmt_resp(struct hostapd_data *hapd, + const u8 *addr, const u8 *frm, + size_t len) +@@ -334,7 +440,22 @@ + { + u8 dialog_token, status_code, bss_termination_delay; + const u8 *pos, *end; ++ int enabled = hapd->conf->bss_transition; ++ struct sta_info *sta; + ++#ifdef CONFIG_MBO ++ if (hapd->conf->mbo_enabled) ++ enabled = 1; ++#endif /* CONFIG_MBO */ ++ if (!enabled) { ++ wpa_printf(MSG_DEBUG, ++ "Ignore BSS Transition Management Response from " ++ MACSTR ++ " since BSS Transition Management is disabled", ++ MAC2STR(addr)); ++ return; ++ } ++ + if (len < 3) { + wpa_printf(MSG_DEBUG, "WNM: Ignore too short BSS Transition Management Response from " + MACSTR, MAC2STR(addr)); +@@ -352,11 +473,23 @@ + "bss_termination_delay=%u", MAC2STR(addr), dialog_token, + status_code, bss_termination_delay); + ++ sta = ap_get_sta(hapd, addr); ++ if (!sta) { ++ wpa_printf(MSG_DEBUG, "Station " MACSTR ++ " not found for received BSS TM Response", ++ MAC2STR(addr)); ++ return; ++ } ++ + if (status_code == WNM_BSS_TM_ACCEPT) { + if (end - pos < ETH_ALEN) { + wpa_printf(MSG_DEBUG, "WNM: not enough room for Target BSSID field"); + return; + } ++ sta->agreed_to_steer = 1; ++ eloop_cancel_timeout(ap_sta_reset_steer_flag_timer, hapd, sta); ++ eloop_register_timeout(2, 0, ap_sta_reset_steer_flag_timer, ++ hapd, sta); + wpa_printf(MSG_DEBUG, "WNM: Target BSSID: " MACSTR, + MAC2STR(pos)); + wpa_msg(hapd->msg_ctx, MSG_INFO, BSS_TM_RESP MACSTR +@@ -366,6 +499,7 @@ + MAC2STR(pos)); + pos += ETH_ALEN; + } else { ++ sta->agreed_to_steer = 0; + wpa_msg(hapd->msg_ctx, MSG_INFO, BSS_TM_RESP MACSTR + " status_code=%u bss_termination_delay=%u", + MAC2STR(addr), status_code, bss_termination_delay); +@@ -376,6 +510,71 @@ + } + + ++static void ieee802_11_rx_wnm_notification_req(struct hostapd_data *hapd, ++ const u8 *addr, const u8 *buf, ++ size_t len) ++{ ++ u8 dialog_token, type; ++ ++ if (len < 2) ++ return; ++ dialog_token = *buf++; ++ type = *buf++; ++ len -= 2; ++ ++ wpa_printf(MSG_DEBUG, ++ "WNM: Received WNM Notification Request frame from " ++ MACSTR " (dialog_token=%u type=%u)", ++ MAC2STR(addr), dialog_token, type); ++ wpa_hexdump(MSG_MSGDUMP, "WNM: Notification Request subelements", ++ buf, len); ++ if (type == WLAN_EID_VENDOR_SPECIFIC) ++ mbo_ap_wnm_notification_req(hapd, addr, buf, len); ++} ++ ++ ++static void ieee802_11_rx_wnm_coloc_intf_report(struct hostapd_data *hapd, ++ const u8 *addr, const u8 *buf, ++ size_t len) ++{ ++ u8 dialog_token; ++ char *hex; ++ size_t hex_len; ++ ++ if (!hapd->conf->coloc_intf_reporting) { ++ wpa_printf(MSG_DEBUG, ++ "WNM: Ignore unexpected Collocated Interference Report from " ++ MACSTR, MAC2STR(addr)); ++ return; ++ } ++ ++ if (len < 1) { ++ wpa_printf(MSG_DEBUG, ++ "WNM: Ignore too short Collocated Interference Report from " ++ MACSTR, MAC2STR(addr)); ++ return; ++ } ++ dialog_token = *buf++; ++ len--; ++ ++ wpa_printf(MSG_DEBUG, ++ "WNM: Received Collocated Interference Report frame from " ++ MACSTR " (dialog_token=%u)", ++ MAC2STR(addr), dialog_token); ++ wpa_hexdump(MSG_MSGDUMP, "WNM: Collocated Interference Report Elements", ++ buf, len); ++ ++ hex_len = 2 * len + 1; ++ hex = os_malloc(hex_len); ++ if (!hex) ++ return; ++ wpa_snprintf_hex(hex, hex_len, buf, len); ++ wpa_msg_ctrl(hapd->msg_ctx, MSG_INFO, COLOC_INTF_REPORT MACSTR " %d %s", ++ MAC2STR(addr), dialog_token, hex); ++ os_free(hex); ++} ++ ++ + int ieee802_11_rx_wnm_action_ap(struct hostapd_data *hapd, + const struct ieee80211_mgmt *mgmt, size_t len) + { +@@ -402,6 +601,14 @@ + case WNM_SLEEP_MODE_REQ: + ieee802_11_rx_wnmsleep_req(hapd, mgmt->sa, payload, plen); + return 0; ++ case WNM_NOTIFICATION_REQ: ++ ieee802_11_rx_wnm_notification_req(hapd, mgmt->sa, payload, ++ plen); ++ return 0; ++ case WNM_COLLOCATED_INTERFERENCE_REPORT: ++ ieee802_11_rx_wnm_coloc_intf_report(hapd, mgmt->sa, payload, ++ plen); ++ return 0; + } + + wpa_printf(MSG_DEBUG, "WNM: Unsupported WNM Action %u from " MACSTR, +@@ -527,7 +734,8 @@ + int wnm_send_bss_tm_req(struct hostapd_data *hapd, struct sta_info *sta, + u8 req_mode, int disassoc_timer, u8 valid_int, + const u8 *bss_term_dur, const char *url, +- const u8 *nei_rep, size_t nei_rep_len) ++ const u8 *nei_rep, size_t nei_rep_len, ++ const u8 *mbo_attrs, size_t mbo_len) + { + u8 *buf, *pos; + struct ieee80211_mgmt *mgmt; +@@ -536,7 +744,7 @@ + wpa_printf(MSG_DEBUG, "WNM: Send BSS Transition Management Request to " + MACSTR " req_mode=0x%x disassoc_timer=%d valid_int=0x%x", + MAC2STR(sta->addr), req_mode, disassoc_timer, valid_int); +- buf = os_zalloc(1000 + nei_rep_len); ++ buf = os_zalloc(1000 + nei_rep_len + mbo_len); + if (buf == NULL) + return -1; + mgmt = (struct ieee80211_mgmt *) buf; +@@ -579,6 +787,11 @@ + pos += nei_rep_len; + } + ++ if (mbo_len > 0) { ++ pos += mbo_add_ie(pos, buf + sizeof(buf) - pos, mbo_attrs, ++ mbo_len); ++ } ++ + if (hostapd_drv_send_mlme(hapd, buf, pos - buf, 0) < 0) { + wpa_printf(MSG_DEBUG, + "Failed to send BSS Transition Management Request frame"); +@@ -594,3 +807,40 @@ + + return 0; + } ++ ++ ++int wnm_send_coloc_intf_req(struct hostapd_data *hapd, struct sta_info *sta, ++ unsigned int auto_report, unsigned int timeout) ++{ ++ u8 buf[100], *pos; ++ struct ieee80211_mgmt *mgmt; ++ u8 dialog_token = 1; ++ ++ if (auto_report > 3 || timeout > 63) ++ return -1; ++ os_memset(buf, 0, sizeof(buf)); ++ mgmt = (struct ieee80211_mgmt *) buf; ++ mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, ++ WLAN_FC_STYPE_ACTION); ++ os_memcpy(mgmt->da, sta->addr, ETH_ALEN); ++ os_memcpy(mgmt->sa, hapd->own_addr, ETH_ALEN); ++ os_memcpy(mgmt->bssid, hapd->own_addr, ETH_ALEN); ++ mgmt->u.action.category = WLAN_ACTION_WNM; ++ mgmt->u.action.u.coloc_intf_req.action = ++ WNM_COLLOCATED_INTERFERENCE_REQ; ++ mgmt->u.action.u.coloc_intf_req.dialog_token = dialog_token; ++ mgmt->u.action.u.coloc_intf_req.req_info = auto_report | (timeout << 2); ++ pos = &mgmt->u.action.u.coloc_intf_req.req_info; ++ pos++; ++ ++ wpa_printf(MSG_DEBUG, "WNM: Sending Collocated Interference Request to " ++ MACSTR " (dialog_token=%u auto_report=%u timeout=%u)", ++ MAC2STR(sta->addr), dialog_token, auto_report, timeout); ++ if (hostapd_drv_send_mlme(hapd, buf, pos - buf, 0) < 0) { ++ wpa_printf(MSG_DEBUG, ++ "WNM: Failed to send Collocated Interference Request frame"); ++ return -1; ++ } ++ ++ return 0; ++} +--- contrib/wpa/src/ap/wnm_ap.h.orig ++++ contrib/wpa/src/ap/wnm_ap.h +@@ -21,6 +21,10 @@ + int wnm_send_bss_tm_req(struct hostapd_data *hapd, struct sta_info *sta, + u8 req_mode, int disassoc_timer, u8 valid_int, + const u8 *bss_term_dur, const char *url, +- const u8 *nei_rep, size_t nei_rep_len); ++ const u8 *nei_rep, size_t nei_rep_len, ++ const u8 *mbo_attrs, size_t mbo_len); ++void ap_sta_reset_steer_flag_timer(void *eloop_ctx, void *timeout_ctx); ++int wnm_send_coloc_intf_req(struct hostapd_data *hapd, struct sta_info *sta, ++ unsigned int auto_report, unsigned int timeout); + + #endif /* WNM_AP_H */ +--- contrib/wpa/src/ap/wpa_auth.c.orig ++++ contrib/wpa/src/ap/wpa_auth.c +@@ -1,6 +1,6 @@ + /* + * IEEE 802.11 RSN / WPA Authenticator +- * Copyright (c) 2004-2015, Jouni Malinen ++ * Copyright (c) 2004-2019, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. +@@ -13,12 +13,17 @@ + #include "utils/state_machine.h" + #include "utils/bitfield.h" + #include "common/ieee802_11_defs.h" ++#include "common/ocv.h" ++#include "crypto/aes.h" + #include "crypto/aes_wrap.h" ++#include "crypto/aes_siv.h" + #include "crypto/crypto.h" + #include "crypto/sha1.h" + #include "crypto/sha256.h" ++#include "crypto/sha384.h" + #include "crypto/random.h" + #include "eapol_auth/eapol_auth_sm.h" ++#include "drivers/driver.h" + #include "ap_config.h" + #include "ieee802_11.h" + #include "wpa_auth.h" +@@ -33,8 +38,14 @@ + + static void wpa_send_eapol_timeout(void *eloop_ctx, void *timeout_ctx); + static int wpa_sm_step(struct wpa_state_machine *sm); +-static int wpa_verify_key_mic(int akmp, struct wpa_ptk *PTK, u8 *data, +- size_t data_len); ++static int wpa_verify_key_mic(int akmp, size_t pmk_len, struct wpa_ptk *PTK, ++ u8 *data, size_t data_len); ++#ifdef CONFIG_FILS ++static int wpa_aead_decrypt(struct wpa_state_machine *sm, struct wpa_ptk *ptk, ++ u8 *buf, size_t buf_len, u16 *_key_data_len); ++static struct wpabuf * fils_prepare_plainbuf(struct wpa_state_machine *sm, ++ const struct wpabuf *hlp); ++#endif /* CONFIG_FILS */ + 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); +@@ -44,7 +55,8 @@ + static int wpa_group_config_group_keys(struct wpa_authenticator *wpa_auth, + struct wpa_group *group); + static int wpa_derive_ptk(struct wpa_state_machine *sm, const u8 *snonce, +- const u8 *pmk, struct wpa_ptk *ptk); ++ const u8 *pmk, unsigned int pmk_len, ++ struct wpa_ptk *ptk); + static void wpa_group_free(struct wpa_authenticator *wpa_auth, + struct wpa_group *group); + static void wpa_group_get(struct wpa_authenticator *wpa_auth, +@@ -51,12 +63,12 @@ + struct wpa_group *group); + static void wpa_group_put(struct wpa_authenticator *wpa_auth, + struct wpa_group *group); ++static u8 * ieee80211w_kde_add(struct wpa_state_machine *sm, u8 *pos); + +-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 */ ++static const u32 eapol_key_timeout_no_retrans = 4000; /* ms */ + + /* TODO: make these configurable */ + static const int dot11RSNAConfigPMKLifetime = 43200; +@@ -67,8 +79,8 @@ + 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); ++ if (wpa_auth->cb->mic_failure_report) ++ return wpa_auth->cb->mic_failure_report(wpa_auth->cb_ctx, addr); + return 0; + } + +@@ -76,8 +88,8 @@ + static inline void wpa_auth_psk_failure_report( + struct wpa_authenticator *wpa_auth, const u8 *addr) + { +- if (wpa_auth->cb.psk_failure_report) +- wpa_auth->cb.psk_failure_report(wpa_auth->cb.ctx, addr); ++ if (wpa_auth->cb->psk_failure_report) ++ wpa_auth->cb->psk_failure_report(wpa_auth->cb_ctx, addr); + } + + +@@ -85,8 +97,8 @@ + 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); ++ if (wpa_auth->cb->set_eapol) ++ wpa_auth->cb->set_eapol(wpa_auth->cb_ctx, addr, var, value); + } + + +@@ -93,9 +105,9 @@ + 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) ++ if (wpa_auth->cb->get_eapol == NULL) + return -1; +- return wpa_auth->cb.get_eapol(wpa_auth->cb.ctx, addr, var); ++ return wpa_auth->cb->get_eapol(wpa_auth->cb_ctx, addr, var); + } + + +@@ -102,12 +114,13 @@ + static inline const u8 * wpa_auth_get_psk(struct wpa_authenticator *wpa_auth, + const u8 *addr, + const u8 *p2p_dev_addr, +- const u8 *prev_psk) ++ const u8 *prev_psk, size_t *psk_len, ++ int *vlan_id) + { +- if (wpa_auth->cb.get_psk == NULL) ++ if (wpa_auth->cb->get_psk == NULL) + return NULL; +- return wpa_auth->cb.get_psk(wpa_auth->cb.ctx, addr, p2p_dev_addr, +- prev_psk); ++ return wpa_auth->cb->get_psk(wpa_auth->cb_ctx, addr, p2p_dev_addr, ++ prev_psk, psk_len, vlan_id); + } + + +@@ -114,9 +127,9 @@ + 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) ++ if (wpa_auth->cb->get_msk == NULL) + return -1; +- return wpa_auth->cb.get_msk(wpa_auth->cb.ctx, addr, msk, len); ++ return wpa_auth->cb->get_msk(wpa_auth->cb_ctx, addr, msk, len); + } + + +@@ -125,10 +138,10 @@ + enum wpa_alg alg, const u8 *addr, int idx, + u8 *key, size_t key_len) + { +- if (wpa_auth->cb.set_key == NULL) ++ 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); ++ return wpa_auth->cb->set_key(wpa_auth->cb_ctx, vlan_id, alg, addr, idx, ++ key, key_len); + } + + +@@ -135,9 +148,9 @@ + 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) ++ if (wpa_auth->cb->get_seqnum == NULL) + return -1; +- return wpa_auth->cb.get_seqnum(wpa_auth->cb.ctx, addr, idx, seq); ++ return wpa_auth->cb->get_seqnum(wpa_auth->cb_ctx, addr, idx, seq); + } + + +@@ -145,10 +158,10 @@ + 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) ++ if (wpa_auth->cb->send_eapol == NULL) + return -1; +- return wpa_auth->cb.send_eapol(wpa_auth->cb.ctx, addr, data, data_len, +- encrypt); ++ return wpa_auth->cb->send_eapol(wpa_auth->cb_ctx, addr, data, data_len, ++ encrypt); + } + + +@@ -156,9 +169,9 @@ + static inline int wpa_auth_start_ampe(struct wpa_authenticator *wpa_auth, + const u8 *addr) + { +- if (wpa_auth->cb.start_ampe == NULL) ++ if (wpa_auth->cb->start_ampe == NULL) + return -1; +- return wpa_auth->cb.start_ampe(wpa_auth->cb.ctx, addr); ++ return wpa_auth->cb->start_ampe(wpa_auth->cb_ctx, addr); + } + #endif /* CONFIG_MESH */ + +@@ -167,9 +180,9 @@ + int (*cb)(struct wpa_state_machine *sm, void *ctx), + void *cb_ctx) + { +- if (wpa_auth->cb.for_each_sta == NULL) ++ if (wpa_auth->cb->for_each_sta == NULL) + return 0; +- return wpa_auth->cb.for_each_sta(wpa_auth->cb.ctx, cb, cb_ctx); ++ return wpa_auth->cb->for_each_sta(wpa_auth->cb_ctx, cb, cb_ctx); + } + + +@@ -177,9 +190,9 @@ + int (*cb)(struct wpa_authenticator *a, void *ctx), + void *cb_ctx) + { +- if (wpa_auth->cb.for_each_auth == NULL) ++ if (wpa_auth->cb->for_each_auth == NULL) + return 0; +- return wpa_auth->cb.for_each_auth(wpa_auth->cb.ctx, cb, cb_ctx); ++ return wpa_auth->cb->for_each_auth(wpa_auth->cb_ctx, cb, cb_ctx); + } + + +@@ -186,9 +199,9 @@ + void wpa_auth_logger(struct wpa_authenticator *wpa_auth, const u8 *addr, + logger_level level, const char *txt) + { +- if (wpa_auth->cb.logger == NULL) ++ if (wpa_auth->cb->logger == NULL) + return; +- wpa_auth->cb.logger(wpa_auth->cb.ctx, addr, level, txt); ++ wpa_auth->cb->logger(wpa_auth->cb_ctx, addr, level, txt); + } + + +@@ -199,7 +212,7 @@ + int maxlen; + va_list ap; + +- if (wpa_auth->cb.logger == NULL) ++ if (wpa_auth->cb->logger == NULL) + return; + + maxlen = os_strlen(fmt) + 100; +@@ -218,33 +231,36 @@ + + + static void wpa_sta_disconnect(struct wpa_authenticator *wpa_auth, +- const u8 *addr) ++ const u8 *addr, u16 reason) + { +- if (wpa_auth->cb.disconnect == NULL) ++ 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); ++ wpa_printf(MSG_DEBUG, "wpa_sta_disconnect STA " MACSTR " (reason %u)", ++ MAC2STR(addr), reason); ++ wpa_auth->cb->disconnect(wpa_auth->cb_ctx, addr, reason); + } + + +-static int wpa_use_aes_cmac(struct wpa_state_machine *sm) ++#ifdef CONFIG_OCV ++static int wpa_channel_info(struct wpa_authenticator *wpa_auth, ++ struct wpa_channel_info *ci) + { +- int ret = 0; +-#ifdef CONFIG_IEEE80211R +- if (wpa_key_mgmt_ft(sm->wpa_key_mgmt)) +- ret = 1; +-#endif /* CONFIG_IEEE80211R */ +-#ifdef CONFIG_IEEE80211W +- if (wpa_key_mgmt_sha256(sm->wpa_key_mgmt)) +- ret = 1; +-#endif /* CONFIG_IEEE80211W */ +- if (sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN) +- ret = 1; +- return ret; ++ if (!wpa_auth->cb->channel_info) ++ return -1; ++ return wpa_auth->cb->channel_info(wpa_auth->cb_ctx, ci); + } ++#endif /* CONFIG_OCV */ + + ++static int wpa_auth_update_vlan(struct wpa_authenticator *wpa_auth, ++ const u8 *addr, int vlan_id) ++{ ++ if (!wpa_auth->cb->update_vlan) ++ return -1; ++ return wpa_auth->cb->update_vlan(wpa_auth->cb_ctx, addr, vlan_id); ++} ++ ++ + static void wpa_rekey_gmk(void *eloop_ctx, void *timeout_ctx) + { + struct wpa_authenticator *wpa_auth = eloop_ctx; +@@ -304,6 +320,19 @@ + } + + ++void wpa_auth_set_ptk_rekey_timer(struct wpa_state_machine *sm) ++{ ++ if (sm && sm->wpa_auth->conf.wpa_ptk_rekey) { ++ wpa_printf(MSG_DEBUG, "WPA: Start PTK rekeying timer for " ++ MACSTR " (%d seconds)", MAC2STR(sm->addr), ++ 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); ++ } ++} ++ ++ + static int wpa_auth_pmksa_clear_cb(struct wpa_state_machine *sm, void *ctx) + { + if (sm->pmksa == ctx) +@@ -339,6 +368,10 @@ + wpa_get_ntp_timestamp(buf + ETH_ALEN); + ptr = (unsigned long) group; + os_memcpy(buf + ETH_ALEN + 8, &ptr, sizeof(ptr)); ++#ifdef TEST_FUZZ ++ os_memset(buf + ETH_ALEN, 0xab, 8); ++ os_memset(buf + ETH_ALEN + 8, 0xcd, sizeof(ptr)); ++#endif /* TEST_FUZZ */ + if (random_get_bytes(rkey, sizeof(rkey)) < 0) + return -1; + +@@ -408,7 +441,8 @@ + */ + struct wpa_authenticator * wpa_init(const u8 *addr, + struct wpa_auth_config *conf, +- struct wpa_auth_callbacks *cb) ++ const struct wpa_auth_callbacks *cb, ++ void *cb_ctx) + { + struct wpa_authenticator *wpa_auth; + +@@ -417,7 +451,8 @@ + 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)); ++ wpa_auth->cb = cb; ++ wpa_auth->cb_ctx = cb_ctx; + + if (wpa_auth_gen_wpa_ie(wpa_auth)) { + wpa_printf(MSG_ERROR, "Could not generate WPA IE."); +@@ -442,7 +477,7 @@ + return NULL; + } + +-#ifdef CONFIG_IEEE80211R ++#ifdef CONFIG_IEEE80211R_AP + 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."); +@@ -452,7 +487,7 @@ + os_free(wpa_auth); + return NULL; + } +-#endif /* CONFIG_IEEE80211R */ ++#endif /* CONFIG_IEEE80211R_AP */ + + if (wpa_auth->conf.wpa_gmk_rekey) { + eloop_register_timeout(wpa_auth->conf.wpa_gmk_rekey, 0, +@@ -505,17 +540,13 @@ + 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 ++#ifdef CONFIG_IEEE80211R_AP + wpa_ft_pmk_cache_deinit(wpa_auth->ft_pmk_cache); + wpa_auth->ft_pmk_cache = NULL; +-#endif /* CONFIG_IEEE80211R */ ++ wpa_ft_deinit(wpa_auth); ++#endif /* CONFIG_IEEE80211R_AP */ + + #ifdef CONFIG_P2P + bitfield_free(wpa_auth->ip_pool); +@@ -598,7 +629,7 @@ + if (wpa_auth == NULL || !wpa_auth->conf.wpa || sm == NULL) + return -1; + +-#ifdef CONFIG_IEEE80211R ++#ifdef CONFIG_IEEE80211R_AP + if (sm->ft_completed) { + wpa_auth_logger(wpa_auth, sm->addr, LOGGER_DEBUG, + "FT authentication already completed - do not " +@@ -605,10 +636,22 @@ + "start 4-way handshake"); + /* Go to PTKINITDONE state to allow GTK rekeying */ + sm->wpa_ptk_state = WPA_PTK_PTKINITDONE; ++ sm->Pair = TRUE; + return 0; + } +-#endif /* CONFIG_IEEE80211R */ ++#endif /* CONFIG_IEEE80211R_AP */ + ++#ifdef CONFIG_FILS ++ if (sm->fils_completed) { ++ wpa_auth_logger(wpa_auth, sm->addr, LOGGER_DEBUG, ++ "FILS authentication already completed - do not start 4-way handshake"); ++ /* Go to PTKINITDONE state to allow GTK rekeying */ ++ sm->wpa_ptk_state = WPA_PTK_PTKINITDONE; ++ sm->Pair = TRUE; ++ return 0; ++ } ++#endif /* CONFIG_FILS */ ++ + if (sm->started) { + os_memset(&sm->key_replay, 0, sizeof(sm->key_replay)); + sm->ReAuthenticationRequest = TRUE; +@@ -659,14 +702,17 @@ + sm->group->GKeyDoneStations--; + sm->GUpdateStationKeys = FALSE; + } +-#ifdef CONFIG_IEEE80211R ++#ifdef CONFIG_IEEE80211R_AP + os_free(sm->assoc_resp_ftie); + wpabuf_free(sm->ft_pending_req_ies); +-#endif /* CONFIG_IEEE80211R */ ++#endif /* CONFIG_IEEE80211R_AP */ + os_free(sm->last_rx_eapol_key); + os_free(sm->wpa_ie); + wpa_group_put(sm->wpa_auth, sm->group); +- os_free(sm); ++#ifdef CONFIG_DPP2 ++ wpabuf_clear_free(sm->dpp_z); ++#endif /* CONFIG_DPP2 */ ++ bin_clear_free(sm, sizeof(*sm)); + } + + +@@ -679,9 +725,10 @@ + 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); ++ if (eloop_deplete_timeout(0, 500000, wpa_rekey_gtk, ++ sm->wpa_auth, NULL) == -1) ++ eloop_register_timeout(0, 500000, wpa_rekey_gtk, sm->wpa_auth, ++ NULL); + } + + eloop_cancel_timeout(wpa_send_eapol_timeout, sm->wpa_auth, sm); +@@ -688,6 +735,9 @@ + 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); ++#ifdef CONFIG_IEEE80211R_AP ++ wpa_ft_sta_deinit(sm); ++#endif /* CONFIG_IEEE80211R_AP */ + 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(). */ +@@ -738,7 +788,7 @@ + } + + +-#ifdef CONFIG_IEEE80211R ++#ifdef CONFIG_IEEE80211R_AP + static int ft_check_msg_2_of_4(struct wpa_authenticator *wpa_auth, + struct wpa_state_machine *sm, + struct wpa_eapol_ie_parse *kde) +@@ -785,7 +835,7 @@ + + return 0; + } +-#endif /* CONFIG_IEEE80211R */ ++#endif /* CONFIG_IEEE80211R_AP */ + + + static int wpa_receive_error_report(struct wpa_authenticator *wpa_auth, +@@ -827,25 +877,44 @@ + struct wpa_ptk PTK; + int ok = 0; + const u8 *pmk = NULL; ++ size_t pmk_len; ++ int vlan_id = 0; + ++ os_memset(&PTK, 0, sizeof(PTK)); + for (;;) { +- if (wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt)) { ++ if (wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt) && ++ !wpa_key_mgmt_sae(sm->wpa_key_mgmt)) { + pmk = wpa_auth_get_psk(sm->wpa_auth, sm->addr, +- sm->p2p_dev_addr, pmk); ++ sm->p2p_dev_addr, pmk, &pmk_len, ++ &vlan_id); + if (pmk == NULL) + break; +- } else ++#ifdef CONFIG_IEEE80211R_AP ++ if (wpa_key_mgmt_ft_psk(sm->wpa_key_mgmt)) { ++ os_memcpy(sm->xxkey, pmk, pmk_len); ++ sm->xxkey_len = pmk_len; ++ } ++#endif /* CONFIG_IEEE80211R_AP */ ++ } else { + pmk = sm->PMK; ++ pmk_len = sm->pmk_len; ++ } + +- wpa_derive_ptk(sm, sm->alt_SNonce, pmk, &PTK); ++ if (wpa_derive_ptk(sm, sm->alt_SNonce, pmk, pmk_len, &PTK) < 0) ++ break; + +- if (wpa_verify_key_mic(sm->wpa_key_mgmt, &PTK, data, data_len) +- == 0) { ++ if (wpa_verify_key_mic(sm->wpa_key_mgmt, pmk_len, &PTK, ++ data, data_len) == 0) { ++ if (sm->PMK != pmk) { ++ os_memcpy(sm->PMK, pmk, pmk_len); ++ sm->pmk_len = pmk_len; ++ } + ok = 1; + break; + } + +- if (!wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt)) ++ if (!wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt) || ++ wpa_key_mgmt_sae(sm->wpa_key_mgmt)) + break; + } + +@@ -858,6 +927,11 @@ + wpa_printf(MSG_DEBUG, + "WPA: Earlier SNonce resulted in matching MIC"); + sm->alt_snonce_valid = 0; ++ ++ if (vlan_id && wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt) && ++ wpa_auth_update_vlan(sm->wpa_auth, sm->addr, vlan_id) < 0) ++ return -1; ++ + os_memcpy(sm->SNonce, sm->alt_SNonce, WPA_NONCE_LEN); + os_memcpy(&sm->PTK, &PTK, sizeof(PTK)); + sm->PTK_valid = TRUE; +@@ -872,39 +946,41 @@ + { + struct ieee802_1x_hdr *hdr; + struct wpa_eapol_key *key; +- struct wpa_eapol_key_192 *key192; + u16 key_info, key_data_length; +- enum { PAIRWISE_2, PAIRWISE_4, GROUP_2, REQUEST, +- SMK_M1, SMK_M3, SMK_ERROR } msg; ++ enum { PAIRWISE_2, PAIRWISE_4, GROUP_2, REQUEST } msg; + char *msgtxt; + struct wpa_eapol_ie_parse kde; +- int ft; +- const u8 *eapol_key_ie, *key_data; +- size_t eapol_key_ie_len, keyhdrlen, mic_len; ++ const u8 *key_data; ++ size_t keyhdrlen, mic_len; ++ u8 *mic; + + if (wpa_auth == NULL || !wpa_auth->conf.wpa || sm == NULL) + return; ++ wpa_hexdump(MSG_MSGDUMP, "WPA: RX EAPOL data", data, data_len); + +- mic_len = wpa_mic_len(sm->wpa_key_mgmt); +- keyhdrlen = mic_len == 24 ? sizeof(*key192) : sizeof(*key); ++ mic_len = wpa_mic_len(sm->wpa_key_mgmt, sm->pmk_len); ++ keyhdrlen = sizeof(*key) + mic_len + 2; + +- if (data_len < sizeof(*hdr) + keyhdrlen) ++ if (data_len < sizeof(*hdr) + keyhdrlen) { ++ wpa_printf(MSG_DEBUG, "WPA: Ignore too short EAPOL-Key frame"); + return; ++ } + + hdr = (struct ieee802_1x_hdr *) data; + key = (struct wpa_eapol_key *) (hdr + 1); +- key192 = (struct wpa_eapol_key_192 *) (hdr + 1); ++ mic = (u8 *) (key + 1); + key_info = WPA_GET_BE16(key->key_info); +- if (mic_len == 24) { +- key_data = (const u8 *) (key192 + 1); +- key_data_length = WPA_GET_BE16(key192->key_data_length); +- } else { +- key_data = (const u8 *) (key + 1); +- key_data_length = WPA_GET_BE16(key->key_data_length); +- } ++ key_data = mic + mic_len + 2; ++ key_data_length = WPA_GET_BE16(mic + mic_len); + 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); ++ " key_info=0x%x type=%u mic_len=%u key_data_length=%u", ++ MAC2STR(sm->addr), key_info, key->type, ++ (unsigned int) mic_len, key_data_length); ++ wpa_hexdump(MSG_MSGDUMP, ++ "WPA: EAPOL-Key header (ending before Key MIC)", ++ key, sizeof(*key)); ++ wpa_hexdump(MSG_MSGDUMP, "WPA: EAPOL-Key Key MIC", ++ mic, mic_len); + if (key_data_length > data_len - sizeof(*hdr) - keyhdrlen) { + wpa_printf(MSG_INFO, "WPA: Invalid EAPOL-Key frame - " + "key_data overflow (%d > %lu)", +@@ -945,25 +1021,20 @@ + /* 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) { ++ if (key_info & WPA_KEY_INFO_SMK_MESSAGE) { ++ wpa_printf(MSG_DEBUG, "WPA: Ignore SMK message"); ++ return; ++ } ++ ++ 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) { ++ } else if (key_data_length == 0 || ++ (mic_len == 0 && (key_info & WPA_KEY_INFO_ENCR_KEY_DATA) && ++ key_data_length == AES_BLOCK_SIZE)) { + msg = PAIRWISE_4; + msgtxt = "4/4 Pairwise"; + } else { +@@ -971,15 +1042,13 @@ + msgtxt = "2/4 Pairwise"; + } + +- /* TODO: key_info type validation for PeerKey */ + if (msg == REQUEST || msg == PAIRWISE_2 || msg == PAIRWISE_4 || + msg == GROUP_2) { + u16 ver = key_info & WPA_KEY_INFO_TYPE_MASK; + if (sm->pairwise == WPA_CIPHER_CCMP || + sm->pairwise == WPA_CIPHER_GCMP) { +- if (wpa_use_aes_cmac(sm) && +- sm->wpa_key_mgmt != WPA_KEY_MGMT_OSEN && +- !wpa_key_mgmt_suite_b(sm->wpa_key_mgmt) && ++ if (wpa_use_cmac(sm->wpa_key_mgmt) && ++ !wpa_use_akm_defined(sm->wpa_key_mgmt) && + ver != WPA_KEY_INFO_TYPE_AES_128_CMAC) { + wpa_auth_logger(wpa_auth, sm->addr, + LOGGER_WARNING, +@@ -989,7 +1058,8 @@ + return; + } + +- if (!wpa_use_aes_cmac(sm) && ++ if (!wpa_use_cmac(sm->wpa_key_mgmt) && ++ !wpa_use_akm_defined(sm->wpa_key_mgmt) && + ver != WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) { + wpa_auth_logger(wpa_auth, sm->addr, + LOGGER_WARNING, +@@ -999,7 +1069,7 @@ + } + } + +- if (wpa_key_mgmt_suite_b(sm->wpa_key_mgmt) && ++ if (wpa_use_akm_defined(sm->wpa_key_mgmt) && + ver != WPA_KEY_INFO_TYPE_AKM_DEFINED) { + wpa_auth_logger(wpa_auth, sm->addr, LOGGER_WARNING, + "did not use EAPOL-Key descriptor version 0 as required for AKM-defined cases"); +@@ -1087,6 +1157,15 @@ + } + + continue_processing: ++#ifdef CONFIG_FILS ++ if (sm->wpa == WPA_VERSION_WPA2 && mic_len == 0 && ++ !(key_info & WPA_KEY_INFO_ENCR_KEY_DATA)) { ++ wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_DEBUG, ++ "WPA: Encr Key Data bit not set even though AEAD cipher is supposed to be used - drop frame"); ++ return; ++ } ++#endif /* CONFIG_FILS */ ++ + switch (msg) { + case PAIRWISE_2: + if (sm->wpa_ptk_state != WPA_PTK_PTKSTART && +@@ -1114,70 +1193,10 @@ + "collect more entropy for random number " + "generation"); + random_mark_pool_ready(); +- wpa_sta_disconnect(wpa_auth, sm->addr); ++ wpa_sta_disconnect(wpa_auth, sm->addr, ++ WLAN_REASON_PREV_AUTH_NOT_VALID); + return; + } +- if (wpa_parse_kde_ies(key_data, key_data_length, &kde) < 0) { +- wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_INFO, +- "received EAPOL-Key msg 2/4 with " +- "invalid Key Data contents"); +- return; +- } +- if (kde.rsn_ie) { +- eapol_key_ie = kde.rsn_ie; +- eapol_key_ie_len = kde.rsn_ie_len; +- } else if (kde.osen) { +- eapol_key_ie = kde.osen; +- eapol_key_ie_len = kde.osen_len; +- } else { +- eapol_key_ie = kde.wpa_ie; +- eapol_key_ie_len = kde.wpa_ie_len; +- } +- ft = sm->wpa == WPA_VERSION_WPA2 && +- wpa_key_mgmt_ft(sm->wpa_key_mgmt); +- if (sm->wpa_ie == NULL || +- wpa_compare_rsn_ie(ft, +- sm->wpa_ie, sm->wpa_ie_len, +- eapol_key_ie, eapol_key_ie_len)) { +- wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO, +- "WPA IE from (Re)AssocReq did not " +- "match with msg 2/4"); +- if (sm->wpa_ie) { +- wpa_hexdump(MSG_DEBUG, "WPA IE in AssocReq", +- sm->wpa_ie, sm->wpa_ie_len); +- } +- wpa_hexdump(MSG_DEBUG, "WPA IE in msg 2/4", +- eapol_key_ie, eapol_key_ie_len); +- /* MLME-DEAUTHENTICATE.request */ +- wpa_sta_disconnect(wpa_auth, sm->addr); +- return; +- } +-#ifdef CONFIG_IEEE80211R +- if (ft && ft_check_msg_2_of_4(wpa_auth, sm, &kde) < 0) { +- wpa_sta_disconnect(wpa_auth, sm->addr); +- return; +- } +-#endif /* CONFIG_IEEE80211R */ +-#ifdef CONFIG_P2P +- if (kde.ip_addr_req && kde.ip_addr_req[0] && +- wpa_auth->ip_pool && WPA_GET_BE32(sm->ip_addr) == 0) { +- int idx; +- wpa_printf(MSG_DEBUG, "P2P: IP address requested in " +- "EAPOL-Key exchange"); +- idx = bitfield_get_first_zero(wpa_auth->ip_pool); +- if (idx >= 0) { +- u32 start = WPA_GET_BE32(wpa_auth->conf. +- ip_addr_start); +- bitfield_set(wpa_auth->ip_pool, idx); +- WPA_PUT_BE32(sm->ip_addr, start + idx); +- wpa_printf(MSG_DEBUG, "P2P: Assigned IP " +- "address %u.%u.%u.%u to " MACSTR, +- sm->ip_addr[0], sm->ip_addr[1], +- sm->ip_addr[2], sm->ip_addr[3], +- MAC2STR(sm->addr)); +- } +- } +-#endif /* CONFIG_P2P */ + break; + case PAIRWISE_4: + if (sm->wpa_ptk_state != WPA_PTK_PTKINITNEGOTIATING || +@@ -1199,28 +1218,6 @@ + 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; + } +@@ -1234,22 +1231,55 @@ + return; + } + +- if (!(key_info & WPA_KEY_INFO_MIC)) { ++ if (!wpa_key_mgmt_fils(sm->wpa_key_mgmt) && ++ !(key_info & WPA_KEY_INFO_MIC)) { + wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO, + "received invalid EAPOL-Key: Key MIC not set"); + return; + } + ++#ifdef CONFIG_FILS ++ if (wpa_key_mgmt_fils(sm->wpa_key_mgmt) && ++ (key_info & WPA_KEY_INFO_MIC)) { ++ wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO, ++ "received invalid EAPOL-Key: Key MIC set"); ++ return; ++ } ++#endif /* CONFIG_FILS */ ++ + sm->MICVerified = FALSE; + if (sm->PTK_valid && !sm->update_snonce) { +- if (wpa_verify_key_mic(sm->wpa_key_mgmt, &sm->PTK, data, +- data_len) && ++ if (mic_len && ++ wpa_verify_key_mic(sm->wpa_key_mgmt, sm->pmk_len, &sm->PTK, ++ data, data_len) && + (msg != PAIRWISE_4 || !sm->alt_snonce_valid || + wpa_try_alt_snonce(sm, data, data_len))) { + wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO, + "received EAPOL-Key with invalid MIC"); ++#ifdef TEST_FUZZ ++ wpa_printf(MSG_INFO, ++ "TEST: Ignore Key MIC failure for fuzz testing"); ++ goto continue_fuzz; ++#endif /* TEST_FUZZ */ + return; + } ++#ifdef CONFIG_FILS ++ if (!mic_len && ++ wpa_aead_decrypt(sm, &sm->PTK, data, data_len, ++ &key_data_length) < 0) { ++ wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO, ++ "received EAPOL-Key with invalid MIC"); ++#ifdef TEST_FUZZ ++ wpa_printf(MSG_INFO, ++ "TEST: Ignore Key MIC failure for fuzz testing"); ++ goto continue_fuzz; ++#endif /* TEST_FUZZ */ ++ return; ++ } ++#endif /* CONFIG_FILS */ ++#ifdef TEST_FUZZ ++ continue_fuzz: ++#endif /* TEST_FUZZ */ + sm->MICVerified = TRUE; + eloop_cancel_timeout(wpa_send_eapol_timeout, wpa_auth, sm); + sm->pending_1_of_4_timeout = 0; +@@ -1272,12 +1302,7 @@ + * even though MAC address KDE is not normally encrypted, + * supplicant is allowed to encrypt it. + */ +- if (msg == SMK_ERROR) { +-#ifdef CONFIG_PEERKEY +- wpa_smk_error(wpa_auth, sm, key_data, key_data_length); +-#endif /* CONFIG_PEERKEY */ +- return; +- } else if (key_info & WPA_KEY_INFO_ERROR) { ++ if (key_info & WPA_KEY_INFO_ERROR) { + if (wpa_receive_error_report( + wpa_auth, sm, + !(key_info & WPA_KEY_INFO_KEY_TYPE)) > 0) +@@ -1287,11 +1312,6 @@ + "received EAPOL-Key Request for new " + "4-Way Handshake"); + wpa_request_new_ptk(sm); +-#ifdef CONFIG_PEERKEY +- } else if (msg == SMK_M1) { +- wpa_smk_m1(wpa_auth, sm, key, key_data, +- key_data_length); +-#endif /* CONFIG_PEERKEY */ + } else if (key_data_length > 0 && + wpa_parse_kde_ies(key_data, key_data_length, + &kde) == 0 && +@@ -1330,18 +1350,10 @@ + wpa_replay_counter_mark_invalid(sm->key_replay, NULL); + } + +-#ifdef CONFIG_PEERKEY +- if (msg == SMK_M3) { +- wpa_smk_m3(wpa_auth, sm, key, key_data, key_data_length); +- return; +- } +-#endif /* CONFIG_PEERKEY */ +- + os_free(sm->last_rx_eapol_key); +- sm->last_rx_eapol_key = os_malloc(data_len); ++ sm->last_rx_eapol_key = os_memdup(data, 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); +@@ -1356,7 +1368,7 @@ + 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 data[ETH_ALEN + WPA_NONCE_LEN + 8 + WPA_GTK_MAX_LEN]; + u8 *pos; + int ret = 0; + +@@ -1367,21 +1379,33 @@ + * is done only at the Authenticator and as such, does not need to be + * exactly same. + */ ++ os_memset(data, 0, sizeof(data)); + 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); ++#ifdef TEST_FUZZ ++ os_memset(pos, 0xef, 8); ++#endif /* TEST_FUZZ */ + pos += 8; +- if (random_get_bytes(pos, 16) < 0) ++ if (random_get_bytes(pos, gtk_len) < 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) ++#ifdef CONFIG_SHA384 ++ if (sha384_prf(gmk, WPA_GMK_LEN, label, data, sizeof(data), ++ gtk, gtk_len) < 0) + ret = -1; +-#endif /* CONFIG_IEEE80211W */ ++#else /* CONFIG_SHA384 */ ++#ifdef CONFIG_SHA256 ++ if (sha256_prf(gmk, WPA_GMK_LEN, label, data, sizeof(data), ++ gtk, gtk_len) < 0) ++ ret = -1; ++#else /* CONFIG_SHA256 */ ++ if (sha1_prf(gmk, WPA_GMK_LEN, label, data, sizeof(data), ++ gtk, gtk_len) < 0) ++ ret = -1; ++#endif /* CONFIG_SHA256 */ ++#endif /* CONFIG_SHA384 */ + + return ret; + } +@@ -1407,7 +1431,6 @@ + { + struct ieee802_1x_hdr *hdr; + struct wpa_eapol_key *key; +- struct wpa_eapol_key_192 *key192; + size_t len, mic_len, keyhdrlen; + int alg; + int key_data_len, pad_len = 0; +@@ -1414,19 +1437,18 @@ + u8 *buf, *pos; + int version, pairwise; + int i; +- u8 *key_data; ++ u8 *key_mic, *key_data; + +- mic_len = wpa_mic_len(sm->wpa_key_mgmt); +- keyhdrlen = mic_len == 24 ? sizeof(*key192) : sizeof(*key); ++ mic_len = wpa_mic_len(sm->wpa_key_mgmt, sm->pmk_len); ++ keyhdrlen = sizeof(*key) + mic_len + 2; + + len = sizeof(struct ieee802_1x_hdr) + keyhdrlen; + + if (force_version) + version = force_version; +- else if (sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN || +- wpa_key_mgmt_suite_b(sm->wpa_key_mgmt)) ++ else if (wpa_use_akm_defined(sm->wpa_key_mgmt)) + version = WPA_KEY_INFO_TYPE_AKM_DEFINED; +- else if (wpa_use_aes_cmac(sm)) ++ else if (wpa_use_cmac(sm->wpa_key_mgmt)) + version = WPA_KEY_INFO_TYPE_AES_128_CMAC; + else if (sm->pairwise != WPA_CIPHER_TKIP) + version = WPA_KEY_INFO_TYPE_HMAC_SHA1_AES; +@@ -1448,8 +1470,7 @@ + key_data_len = kde_len; + + if ((version == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES || +- sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN || +- wpa_key_mgmt_suite_b(sm->wpa_key_mgmt) || ++ wpa_use_aes_key_wrap(sm->wpa_key_mgmt) || + version == WPA_KEY_INFO_TYPE_AES_128_CMAC) && encr) { + pad_len = key_data_len % 8; + if (pad_len) +@@ -1458,6 +1479,8 @@ + } + + len += key_data_len; ++ if (!mic_len && encr) ++ len += AES_BLOCK_SIZE; + + hdr = os_zalloc(len); + if (hdr == NULL) +@@ -1466,7 +1489,7 @@ + hdr->type = IEEE802_1X_TYPE_EAPOL_KEY; + hdr->length = host_to_be16(len - sizeof(*hdr)); + key = (struct wpa_eapol_key *) (hdr + 1); +- key192 = (struct wpa_eapol_key_192 *) (hdr + 1); ++ key_mic = (u8 *) (key + 1); + key_data = ((u8 *) (hdr + 1)) + keyhdrlen; + + key->type = sm->wpa == WPA_VERSION_WPA2 ? +@@ -1479,11 +1502,11 @@ + 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) ++ if (sm->wpa == WPA_VERSION_WPA2 && !pairwise) + WPA_PUT_BE16(key->key_length, 0); ++ else ++ WPA_PUT_BE16(key->key_length, wpa_cipher_key_len(alg)); + +- /* 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, +@@ -1505,10 +1528,31 @@ + + if (kde && !encr) { + os_memcpy(key_data, kde, kde_len); +- if (mic_len == 24) +- WPA_PUT_BE16(key192->key_data_length, kde_len); +- else +- WPA_PUT_BE16(key->key_data_length, kde_len); ++ WPA_PUT_BE16(key_mic + mic_len, kde_len); ++#ifdef CONFIG_FILS ++ } else if (!mic_len && kde) { ++ const u8 *aad[1]; ++ size_t aad_len[1]; ++ ++ WPA_PUT_BE16(key_mic, AES_BLOCK_SIZE + kde_len); ++ wpa_hexdump_key(MSG_DEBUG, "Plaintext EAPOL-Key Key Data", ++ kde, kde_len); ++ ++ wpa_hexdump_key(MSG_DEBUG, "WPA: KEK", ++ sm->PTK.kek, sm->PTK.kek_len); ++ /* AES-SIV AAD from EAPOL protocol version field (inclusive) to ++ * to Key Data (exclusive). */ ++ aad[0] = (u8 *) hdr; ++ aad_len[0] = key_mic + 2 - (u8 *) hdr; ++ if (aes_siv_encrypt(sm->PTK.kek, sm->PTK.kek_len, kde, kde_len, ++ 1, aad, aad_len, key_mic + 2) < 0) { ++ wpa_printf(MSG_DEBUG, "WPA: AES-SIV encryption failed"); ++ return; ++ } ++ ++ wpa_hexdump(MSG_DEBUG, "WPA: Encrypted Key Data from SIV", ++ key_mic + 2, AES_BLOCK_SIZE + kde_len); ++#endif /* CONFIG_FILS */ + } else if (encr && kde) { + buf = os_zalloc(key_data_len); + if (buf == NULL) { +@@ -1525,9 +1569,11 @@ + wpa_hexdump_key(MSG_DEBUG, "Plaintext EAPOL-Key Key Data", + buf, key_data_len); + if (version == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES || +- sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN || +- wpa_key_mgmt_suite_b(sm->wpa_key_mgmt) || ++ wpa_use_aes_key_wrap(sm->wpa_key_mgmt) || + version == WPA_KEY_INFO_TYPE_AES_128_CMAC) { ++ wpa_printf(MSG_DEBUG, ++ "WPA: Encrypt Key Data using AES-WRAP (KEK length %u)", ++ (unsigned int) sm->PTK.kek_len); + if (aes_wrap(sm->PTK.kek, sm->PTK.kek_len, + (key_data_len - 8) / 8, buf, key_data)) { + os_free(hdr); +@@ -1534,15 +1580,13 @@ + os_free(buf); + return; + } +- if (mic_len == 24) +- WPA_PUT_BE16(key192->key_data_length, +- key_data_len); +- else +- WPA_PUT_BE16(key->key_data_length, +- key_data_len); ++ WPA_PUT_BE16(key_mic + mic_len, key_data_len); + #ifndef CONFIG_NO_RC4 + } else if (sm->PTK.kek_len == 16) { + u8 ek[32]; ++ ++ wpa_printf(MSG_DEBUG, ++ "WPA: Encrypt Key Data using RC4"); + os_memcpy(key->key_iv, + sm->group->Counter + WPA_NONCE_LEN - 16, 16); + inc_byte_array(sm->group->Counter, WPA_NONCE_LEN); +@@ -1550,12 +1594,7 @@ + os_memcpy(ek + 16, sm->PTK.kek, sm->PTK.kek_len); + os_memcpy(key_data, buf, key_data_len); + rc4_skip(ek, 32, 256, key_data, key_data_len); +- if (mic_len == 24) +- WPA_PUT_BE16(key192->key_data_length, +- key_data_len); +- else +- WPA_PUT_BE16(key->key_data_length, +- key_data_len); ++ WPA_PUT_BE16(key_mic + mic_len, key_data_len); + #endif /* CONFIG_NO_RC4 */ + } else { + os_free(hdr); +@@ -1566,9 +1605,7 @@ + } + + if (key_info & WPA_KEY_INFO_MIC) { +- u8 *key_mic; +- +- if (!sm->PTK_valid) { ++ if (!sm->PTK_valid || !mic_len) { + wpa_auth_logger(wpa_auth, sm->addr, LOGGER_DEBUG, + "PTK not valid when sending EAPOL-Key " + "frame"); +@@ -1576,10 +1613,12 @@ + return; + } + +- key_mic = key192->key_mic; /* same offset for key and key192 */ +- wpa_eapol_key_mic(sm->PTK.kck, sm->PTK.kck_len, +- sm->wpa_key_mgmt, version, +- (u8 *) hdr, len, key_mic); ++ if (wpa_eapol_key_mic(sm->PTK.kck, sm->PTK.kck_len, ++ sm->wpa_key_mgmt, version, ++ (u8 *) hdr, len, key_mic) < 0) { ++ os_free(hdr); ++ return; ++ } + #ifdef CONFIG_TESTING_OPTIONS + if (!pairwise && + wpa_auth->conf.corrupt_gtk_rekey_mic_probability > 0.0 && +@@ -1608,7 +1647,7 @@ + { + int timeout_ms; + int pairwise = key_info & WPA_KEY_INFO_KEY_TYPE; +- int ctr; ++ u32 ctr; + + if (sm == NULL) + return; +@@ -1622,25 +1661,30 @@ + eapol_key_timeout_first_group; + else + timeout_ms = eapol_key_timeout_subseq; ++ if (wpa_auth->conf.wpa_disable_eapol_key_retries && ++ (!pairwise || (key_info & WPA_KEY_INFO_MIC))) ++ timeout_ms = eapol_key_timeout_no_retrans; + if (pairwise && ctr == 1 && !(key_info & WPA_KEY_INFO_MIC)) + sm->pending_1_of_4_timeout = 1; ++#ifdef TEST_FUZZ ++ timeout_ms = 1; ++#endif /* TEST_FUZZ */ + wpa_printf(MSG_DEBUG, "WPA: Use EAPOL-Key timeout of %u ms (retry " +- "counter %d)", timeout_ms, ctr); ++ "counter %u)", timeout_ms, ctr); + eloop_register_timeout(timeout_ms / 1000, (timeout_ms % 1000) * 1000, + wpa_send_eapol_timeout, wpa_auth, sm); + } + + +-static int wpa_verify_key_mic(int akmp, struct wpa_ptk *PTK, u8 *data, +- size_t data_len) ++static int wpa_verify_key_mic(int akmp, size_t pmk_len, struct wpa_ptk *PTK, ++ u8 *data, size_t data_len) + { + struct ieee802_1x_hdr *hdr; + struct wpa_eapol_key *key; +- struct wpa_eapol_key_192 *key192; + u16 key_info; + int ret = 0; +- u8 mic[WPA_EAPOL_KEY_MIC_MAX_LEN]; +- size_t mic_len = wpa_mic_len(akmp); ++ u8 mic[WPA_EAPOL_KEY_MIC_MAX_LEN], *mic_pos; ++ size_t mic_len = wpa_mic_len(akmp, pmk_len); + + if (data_len < sizeof(*hdr) + sizeof(*key)) + return -1; +@@ -1647,16 +1691,16 @@ + + hdr = (struct ieee802_1x_hdr *) data; + key = (struct wpa_eapol_key *) (hdr + 1); +- key192 = (struct wpa_eapol_key_192 *) (hdr + 1); ++ mic_pos = (u8 *) (key + 1); + key_info = WPA_GET_BE16(key->key_info); +- os_memcpy(mic, key192->key_mic, mic_len); +- os_memset(key192->key_mic, 0, mic_len); ++ os_memcpy(mic, mic_pos, mic_len); ++ os_memset(mic_pos, 0, mic_len); + if (wpa_eapol_key_mic(PTK->kck, PTK->kck_len, akmp, + key_info & WPA_KEY_INFO_TYPE_MASK, +- data, data_len, key192->key_mic) || +- os_memcmp_const(mic, key192->key_mic, mic_len) != 0) ++ data, data_len, mic_pos) || ++ os_memcmp_const(mic, mic_pos, mic_len) != 0) + ret = -1; +- os_memcpy(key192->key_mic, mic, mic_len); ++ os_memcpy(mic_pos, mic, mic_len); + return ret; + } + +@@ -1665,7 +1709,10 @@ + { + 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); ++ if (wpa_auth_set_key(sm->wpa_auth, 0, WPA_ALG_NONE, sm->addr, 0, NULL, ++ 0)) ++ wpa_printf(MSG_DEBUG, ++ "RSN: PTK removal from the driver failed"); + sm->pairwise_set = FALSE; + eloop_cancel_timeout(wpa_rekey_ptk, sm->wpa_auth, sm); + } +@@ -1696,6 +1743,14 @@ + case WPA_DEAUTH: + case WPA_DISASSOC: + sm->DeauthenticationRequest = TRUE; ++#ifdef CONFIG_IEEE80211R_AP ++ os_memset(sm->PMK, 0, sizeof(sm->PMK)); ++ sm->pmk_len = 0; ++ os_memset(sm->xxkey, 0, sizeof(sm->xxkey)); ++ sm->xxkey_len = 0; ++ os_memset(sm->pmk_r1, 0, sizeof(sm->pmk_r1)); ++ sm->pmk_r1_len = 0; ++#endif /* CONFIG_IEEE80211R_AP */ + break; + case WPA_REAUTH: + case WPA_REAUTH_EAPOL: +@@ -1729,7 +1784,7 @@ + sm->ReAuthenticationRequest = TRUE; + break; + case WPA_ASSOC_FT: +-#ifdef CONFIG_IEEE80211R ++#ifdef CONFIG_IEEE80211R_AP + wpa_printf(MSG_DEBUG, "FT: Retry PTK configuration " + "after association"); + wpa_ft_install_ptk(sm); +@@ -1736,20 +1791,39 @@ + + /* Using FT protocol, not WPA auth state machine */ + sm->ft_completed = 1; ++ wpa_auth_set_ptk_rekey_timer(sm); + return 0; +-#else /* CONFIG_IEEE80211R */ ++#else /* CONFIG_IEEE80211R_AP */ + break; +-#endif /* CONFIG_IEEE80211R */ ++#endif /* CONFIG_IEEE80211R_AP */ ++ case WPA_ASSOC_FILS: ++#ifdef CONFIG_FILS ++ wpa_printf(MSG_DEBUG, ++ "FILS: TK configuration after association"); ++ fils_set_tk(sm); ++ sm->fils_completed = 1; ++ return 0; ++#else /* CONFIG_FILS */ ++ break; ++#endif /* CONFIG_FILS */ ++ case WPA_DRV_STA_REMOVED: ++ sm->tk_already_set = FALSE; ++ return 0; + } + +-#ifdef CONFIG_IEEE80211R ++#ifdef CONFIG_IEEE80211R_AP + sm->ft_completed = 0; +-#endif /* CONFIG_IEEE80211R */ ++#endif /* CONFIG_IEEE80211R_AP */ + + #ifdef CONFIG_IEEE80211W + if (sm->mgmt_frame_prot && event == WPA_AUTH) + remove_ptk = 0; + #endif /* CONFIG_IEEE80211W */ ++#ifdef CONFIG_FILS ++ if (wpa_key_mgmt_fils(sm->wpa_key_mgmt) && ++ (event == WPA_AUTH || event == WPA_ASSOC)) ++ remove_ptk = 0; ++#endif /* CONFIG_FILS */ + + if (remove_ptk) { + sm->PTK_valid = FALSE; +@@ -1794,7 +1868,9 @@ + 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)) { ++ if (wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt) || ++ sm->wpa_key_mgmt == WPA_KEY_MGMT_DPP || ++ sm->wpa_key_mgmt == WPA_KEY_MGMT_OWE) { + wpa_auth_set_eapol(sm->wpa_auth, sm->addr, + WPA_EAPOL_authorized, 0); + } +@@ -1803,9 +1879,14 @@ + + SM_STATE(WPA_PTK, DISCONNECT) + { ++ u16 reason = sm->disconnect_reason; ++ + SM_ENTRY_MA(WPA_PTK, DISCONNECT, wpa_ptk); + sm->Disconnect = FALSE; +- wpa_sta_disconnect(sm->wpa_auth, sm->addr); ++ sm->disconnect_reason = 0; ++ if (!reason) ++ reason = WLAN_REASON_PREV_AUTH_NOT_VALID; ++ wpa_sta_disconnect(sm->wpa_auth, sm->addr, reason); + } + + +@@ -1914,25 +1995,54 @@ + size_t len = 2 * PMK_LEN; + + SM_ENTRY_MA(WPA_PTK, INITPMK, wpa_ptk); +-#ifdef CONFIG_IEEE80211R ++#ifdef CONFIG_IEEE80211R_AP + sm->xxkey_len = 0; +-#endif /* CONFIG_IEEE80211R */ ++#endif /* CONFIG_IEEE80211R_AP */ + if (sm->pmksa) { + wpa_printf(MSG_DEBUG, "WPA: PMK from PMKSA cache"); +- os_memcpy(sm->PMK, sm->pmksa->pmk, PMK_LEN); ++ os_memcpy(sm->PMK, sm->pmksa->pmk, sm->pmksa->pmk_len); ++ sm->pmk_len = sm->pmksa->pmk_len; ++#ifdef CONFIG_DPP ++ } else if (sm->wpa_key_mgmt == WPA_KEY_MGMT_DPP) { ++ wpa_printf(MSG_DEBUG, ++ "DPP: No PMKSA cache entry for STA - reject connection"); ++ sm->Disconnect = TRUE; ++ sm->disconnect_reason = WLAN_REASON_INVALID_PMKID; ++ return; ++#endif /* CONFIG_DPP */ + } else if (wpa_auth_get_msk(sm->wpa_auth, sm->addr, msk, &len) == 0) { ++ unsigned int pmk_len; ++ ++ if (wpa_key_mgmt_sha384(sm->wpa_key_mgmt)) ++ pmk_len = PMK_LEN_SUITE_B_192; ++ else ++ pmk_len = PMK_LEN; + 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 ++ "(MSK len=%lu PMK len=%u)", (unsigned long) len, ++ pmk_len); ++ if (len < pmk_len) { ++ wpa_printf(MSG_DEBUG, ++ "WPA: MSK not long enough (%u) to create PMK (%u)", ++ (unsigned int) len, (unsigned int) pmk_len); ++ sm->Disconnect = TRUE; ++ return; ++ } ++ os_memcpy(sm->PMK, msk, pmk_len); ++ sm->pmk_len = pmk_len; ++#ifdef CONFIG_IEEE80211R_AP + if (len >= 2 * PMK_LEN) { +- os_memcpy(sm->xxkey, msk + PMK_LEN, PMK_LEN); +- sm->xxkey_len = PMK_LEN; ++ if (wpa_key_mgmt_sha384(sm->wpa_key_mgmt)) { ++ os_memcpy(sm->xxkey, msk, SHA384_MAC_LEN); ++ sm->xxkey_len = SHA384_MAC_LEN; ++ } else { ++ os_memcpy(sm->xxkey, msk + PMK_LEN, PMK_LEN); ++ sm->xxkey_len = PMK_LEN; ++ } + } +-#endif /* CONFIG_IEEE80211R */ ++#endif /* CONFIG_IEEE80211R_AP */ + } else { + wpa_printf(MSG_DEBUG, "WPA: Could not get PMK, get_msk: %p", +- sm->wpa_auth->cb.get_msk); ++ sm->wpa_auth->cb->get_msk); + sm->Disconnect = TRUE; + return; + } +@@ -1954,15 +2064,30 @@ + SM_STATE(WPA_PTK, INITPSK) + { + const u8 *psk; ++ size_t psk_len; ++ + SM_ENTRY_MA(WPA_PTK, INITPSK, wpa_ptk); +- psk = wpa_auth_get_psk(sm->wpa_auth, sm->addr, sm->p2p_dev_addr, NULL); ++ psk = wpa_auth_get_psk(sm->wpa_auth, sm->addr, sm->p2p_dev_addr, NULL, ++ &psk_len, NULL); + if (psk) { +- os_memcpy(sm->PMK, psk, PMK_LEN); +-#ifdef CONFIG_IEEE80211R ++ os_memcpy(sm->PMK, psk, psk_len); ++ sm->pmk_len = psk_len; ++#ifdef CONFIG_IEEE80211R_AP + os_memcpy(sm->xxkey, psk, PMK_LEN); + sm->xxkey_len = PMK_LEN; +-#endif /* CONFIG_IEEE80211R */ ++#endif /* CONFIG_IEEE80211R_AP */ + } ++#ifdef CONFIG_SAE ++ if (wpa_auth_uses_sae(sm) && sm->pmksa) { ++ wpa_printf(MSG_DEBUG, "SAE: PMK from PMKSA cache"); ++ os_memcpy(sm->PMK, sm->pmksa->pmk, sm->pmksa->pmk_len); ++ sm->pmk_len = sm->pmksa->pmk_len; ++#ifdef CONFIG_IEEE80211R_AP ++ os_memcpy(sm->xxkey, sm->pmksa->pmk, sm->pmksa->pmk_len); ++ sm->xxkey_len = sm->pmksa->pmk_len; ++#endif /* CONFIG_IEEE80211R_AP */ ++ } ++#endif /* CONFIG_SAE */ + sm->req_replay_counter_used = 0; + } + +@@ -1978,7 +2103,7 @@ + sm->alt_snonce_valid = FALSE; + + sm->TimeoutCtr++; +- if (sm->TimeoutCtr > (int) dot11RSNAConfigPairwiseUpdateCount) { ++ if (sm->TimeoutCtr > sm->wpa_auth->conf.wpa_pairwise_update_count) { + /* No point in sending the EAPOL-Key - we will disconnect + * immediately following this. */ + return; +@@ -1987,11 +2112,23 @@ + 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. ++ * For infrastructure BSS cases, it is better for the AP not to include ++ * the PMKID KDE in EAPOL-Key msg 1/4 since it could be used to initiate ++ * offline search for the passphrase/PSK without having to be able to ++ * capture a 4-way handshake from a STA that has access to the network. ++ * ++ * For IBSS cases, addition of PMKID KDE could be considered even with ++ * WPA2-PSK cases that use multiple PSKs, but only if there is a single ++ * possible PSK for this STA. However, this should not be done unless ++ * there is support for using that information on the supplicant side. ++ * The concern about exposing PMKID unnecessarily in infrastructure BSS ++ * cases would also apply here, but at least in the IBSS case, this ++ * would cover a potential real use case. + */ + if (sm->wpa == WPA_VERSION_WPA2 && +- wpa_key_mgmt_wpa_ieee8021x(sm->wpa_key_mgmt) && ++ (wpa_key_mgmt_wpa_ieee8021x(sm->wpa_key_mgmt) || ++ (sm->wpa_key_mgmt == WPA_KEY_MGMT_OWE && sm->pmksa) || ++ wpa_key_mgmt_sae(sm->wpa_key_mgmt)) && + sm->wpa_key_mgmt != WPA_KEY_MGMT_OSEN) { + pmkid = buf; + pmkid_len = 2 + RSN_SELECTOR_LEN + PMKID_LEN; +@@ -1999,19 +2136,65 @@ + pmkid[1] = RSN_SELECTOR_LEN + PMKID_LEN; + RSN_SELECTOR_PUT(&pmkid[2], RSN_KEY_DATA_PMKID); + if (sm->pmksa) { ++ wpa_hexdump(MSG_DEBUG, ++ "RSN: Message 1/4 PMKID from PMKSA entry", ++ sm->pmksa->pmkid, PMKID_LEN); + os_memcpy(&pmkid[2 + RSN_SELECTOR_LEN], + sm->pmksa->pmkid, PMKID_LEN); + } else if (wpa_key_mgmt_suite_b(sm->wpa_key_mgmt)) { + /* No KCK available to derive PMKID */ ++ wpa_printf(MSG_DEBUG, ++ "RSN: No KCK available to derive PMKID for message 1/4"); + pmkid = NULL; ++#ifdef CONFIG_FILS ++ } else if (wpa_key_mgmt_fils(sm->wpa_key_mgmt)) { ++ if (sm->pmkid_set) { ++ wpa_hexdump(MSG_DEBUG, ++ "RSN: Message 1/4 PMKID from FILS/ERP", ++ sm->pmkid, PMKID_LEN); ++ os_memcpy(&pmkid[2 + RSN_SELECTOR_LEN], ++ sm->pmkid, PMKID_LEN); ++ } else { ++ /* No PMKID available */ ++ wpa_printf(MSG_DEBUG, ++ "RSN: No FILS/ERP PMKID available for message 1/4"); ++ pmkid = NULL; ++ } ++#endif /* CONFIG_FILS */ ++#ifdef CONFIG_IEEE80211R_AP ++ } else if (wpa_key_mgmt_ft(sm->wpa_key_mgmt) && ++ sm->ft_completed) { ++ wpa_printf(MSG_DEBUG, ++ "FT: No PMKID in message 1/4 when using FT protocol"); ++ pmkid = NULL; ++ pmkid_len = 0; ++#endif /* CONFIG_IEEE80211R_AP */ ++#ifdef CONFIG_SAE ++ } else if (wpa_key_mgmt_sae(sm->wpa_key_mgmt)) { ++ if (sm->pmkid_set) { ++ wpa_hexdump(MSG_DEBUG, ++ "RSN: Message 1/4 PMKID from SAE", ++ sm->pmkid, PMKID_LEN); ++ os_memcpy(&pmkid[2 + RSN_SELECTOR_LEN], ++ sm->pmkid, PMKID_LEN); ++ } else { ++ /* No PMKID available */ ++ wpa_printf(MSG_DEBUG, ++ "RSN: No SAE PMKID available for message 1/4"); ++ pmkid = NULL; ++ } ++#endif /* CONFIG_SAE */ + } else { + /* + * Calculate PMKID since no PMKSA cache entry was + * available with pre-calculated PMKID. + */ +- rsn_pmkid(sm->PMK, PMK_LEN, sm->wpa_auth->addr, ++ rsn_pmkid(sm->PMK, sm->pmk_len, sm->wpa_auth->addr, + sm->addr, &pmkid[2 + RSN_SELECTOR_LEN], +- wpa_key_mgmt_sha256(sm->wpa_key_mgmt)); ++ sm->wpa_key_mgmt); ++ wpa_hexdump(MSG_DEBUG, ++ "RSN: Message 1/4 PMKID derived from PMK", ++ &pmkid[2 + RSN_SELECTOR_LEN], PMKID_LEN); + } + } + wpa_send_eapol(sm->wpa_auth, sm, +@@ -2021,53 +2204,682 @@ + + + static int wpa_derive_ptk(struct wpa_state_machine *sm, const u8 *snonce, +- const u8 *pmk, struct wpa_ptk *ptk) ++ const u8 *pmk, unsigned int pmk_len, ++ struct wpa_ptk *ptk) + { +-#ifdef CONFIG_IEEE80211R +- if (wpa_key_mgmt_ft(sm->wpa_key_mgmt)) +- return wpa_auth_derive_ptk_ft(sm, pmk, ptk); +-#endif /* CONFIG_IEEE80211R */ ++ const u8 *z = NULL; ++ size_t z_len = 0; + +- return wpa_pmk_to_ptk(pmk, PMK_LEN, "Pairwise key expansion", ++#ifdef CONFIG_IEEE80211R_AP ++ if (wpa_key_mgmt_ft(sm->wpa_key_mgmt)) { ++ if (sm->ft_completed) { ++ u8 ptk_name[WPA_PMK_NAME_LEN]; ++ ++ return wpa_pmk_r1_to_ptk(sm->pmk_r1, sm->pmk_r1_len, ++ sm->SNonce, sm->ANonce, ++ sm->addr, sm->wpa_auth->addr, ++ sm->pmk_r1_name, ++ ptk, ptk_name, ++ sm->wpa_key_mgmt, ++ sm->pairwise); ++ } ++ return wpa_auth_derive_ptk_ft(sm, ptk); ++ } ++#endif /* CONFIG_IEEE80211R_AP */ ++ ++#ifdef CONFIG_DPP2 ++ if (sm->wpa_key_mgmt == WPA_KEY_MGMT_DPP && sm->dpp_z) { ++ z = wpabuf_head(sm->dpp_z); ++ z_len = wpabuf_len(sm->dpp_z); ++ } ++#endif /* CONFIG_DPP2 */ ++ ++ return wpa_pmk_to_ptk(pmk, pmk_len, "Pairwise key expansion", + sm->wpa_auth->addr, sm->addr, sm->ANonce, snonce, +- ptk, sm->wpa_key_mgmt, sm->pairwise); ++ ptk, sm->wpa_key_mgmt, sm->pairwise, z, z_len); + } + + ++#ifdef CONFIG_FILS ++ ++int fils_auth_pmk_to_ptk(struct wpa_state_machine *sm, const u8 *pmk, ++ size_t pmk_len, const u8 *snonce, const u8 *anonce, ++ const u8 *dhss, size_t dhss_len, ++ struct wpabuf *g_sta, struct wpabuf *g_ap) ++{ ++ u8 ick[FILS_ICK_MAX_LEN]; ++ size_t ick_len; ++ int res; ++ u8 fils_ft[FILS_FT_MAX_LEN]; ++ size_t fils_ft_len = 0; ++ ++ res = fils_pmk_to_ptk(pmk, pmk_len, sm->addr, sm->wpa_auth->addr, ++ snonce, anonce, dhss, dhss_len, ++ &sm->PTK, ick, &ick_len, ++ sm->wpa_key_mgmt, sm->pairwise, ++ fils_ft, &fils_ft_len); ++ if (res < 0) ++ return res; ++ sm->PTK_valid = TRUE; ++ sm->tk_already_set = FALSE; ++ ++#ifdef CONFIG_IEEE80211R_AP ++ if (fils_ft_len) { ++ struct wpa_authenticator *wpa_auth = sm->wpa_auth; ++ struct wpa_auth_config *conf = &wpa_auth->conf; ++ u8 pmk_r0[PMK_LEN_MAX], pmk_r0_name[WPA_PMK_NAME_LEN]; ++ int use_sha384 = wpa_key_mgmt_sha384(sm->wpa_key_mgmt); ++ size_t pmk_r0_len = use_sha384 ? SHA384_MAC_LEN : PMK_LEN; ++ ++ if (wpa_derive_pmk_r0(fils_ft, fils_ft_len, ++ conf->ssid, conf->ssid_len, ++ conf->mobility_domain, ++ conf->r0_key_holder, ++ conf->r0_key_holder_len, ++ sm->addr, pmk_r0, pmk_r0_name, ++ use_sha384) < 0) ++ return -1; ++ ++ wpa_hexdump_key(MSG_DEBUG, "FILS+FT: PMK-R0", ++ pmk_r0, pmk_r0_len); ++ wpa_hexdump(MSG_DEBUG, "FILS+FT: PMKR0Name", ++ pmk_r0_name, WPA_PMK_NAME_LEN); ++ wpa_ft_store_pmk_fils(sm, pmk_r0, pmk_r0_name); ++ os_memset(fils_ft, 0, sizeof(fils_ft)); ++ ++ res = wpa_derive_pmk_r1_name(pmk_r0_name, conf->r1_key_holder, ++ sm->addr, sm->pmk_r1_name, ++ use_sha384); ++ os_memset(pmk_r0, 0, PMK_LEN_MAX); ++ if (res < 0) ++ return -1; ++ wpa_hexdump(MSG_DEBUG, "FILS+FT: PMKR1Name", sm->pmk_r1_name, ++ WPA_PMK_NAME_LEN); ++ sm->pmk_r1_name_valid = 1; ++ } ++#endif /* CONFIG_IEEE80211R_AP */ ++ ++ res = fils_key_auth_sk(ick, ick_len, snonce, anonce, ++ sm->addr, sm->wpa_auth->addr, ++ g_sta ? wpabuf_head(g_sta) : NULL, ++ g_sta ? wpabuf_len(g_sta) : 0, ++ g_ap ? wpabuf_head(g_ap) : NULL, ++ g_ap ? wpabuf_len(g_ap) : 0, ++ sm->wpa_key_mgmt, sm->fils_key_auth_sta, ++ sm->fils_key_auth_ap, ++ &sm->fils_key_auth_len); ++ os_memset(ick, 0, sizeof(ick)); ++ ++ /* Store nonces for (Re)Association Request/Response frame processing */ ++ os_memcpy(sm->SNonce, snonce, FILS_NONCE_LEN); ++ os_memcpy(sm->ANonce, anonce, FILS_NONCE_LEN); ++ ++ return res; ++} ++ ++ ++static int wpa_aead_decrypt(struct wpa_state_machine *sm, struct wpa_ptk *ptk, ++ u8 *buf, size_t buf_len, u16 *_key_data_len) ++{ ++ struct ieee802_1x_hdr *hdr; ++ struct wpa_eapol_key *key; ++ u8 *pos; ++ u16 key_data_len; ++ u8 *tmp; ++ const u8 *aad[1]; ++ size_t aad_len[1]; ++ ++ hdr = (struct ieee802_1x_hdr *) buf; ++ key = (struct wpa_eapol_key *) (hdr + 1); ++ pos = (u8 *) (key + 1); ++ key_data_len = WPA_GET_BE16(pos); ++ if (key_data_len < AES_BLOCK_SIZE || ++ key_data_len > buf_len - sizeof(*hdr) - sizeof(*key) - 2) { ++ wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_INFO, ++ "No room for AES-SIV data in the frame"); ++ return -1; ++ } ++ pos += 2; /* Pointing at the Encrypted Key Data field */ ++ ++ tmp = os_malloc(key_data_len); ++ if (!tmp) ++ return -1; ++ ++ /* AES-SIV AAD from EAPOL protocol version field (inclusive) to ++ * to Key Data (exclusive). */ ++ aad[0] = buf; ++ aad_len[0] = pos - buf; ++ if (aes_siv_decrypt(ptk->kek, ptk->kek_len, pos, key_data_len, ++ 1, aad, aad_len, tmp) < 0) { ++ wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_INFO, ++ "Invalid AES-SIV data in the frame"); ++ bin_clear_free(tmp, key_data_len); ++ return -1; ++ } ++ ++ /* AEAD decryption and validation completed successfully */ ++ key_data_len -= AES_BLOCK_SIZE; ++ wpa_hexdump_key(MSG_DEBUG, "WPA: Decrypted Key Data", ++ tmp, key_data_len); ++ ++ /* Replace Key Data field with the decrypted version */ ++ os_memcpy(pos, tmp, key_data_len); ++ pos -= 2; /* Key Data Length field */ ++ WPA_PUT_BE16(pos, key_data_len); ++ bin_clear_free(tmp, key_data_len); ++ if (_key_data_len) ++ *_key_data_len = key_data_len; ++ return 0; ++} ++ ++ ++const u8 * wpa_fils_validate_fils_session(struct wpa_state_machine *sm, ++ const u8 *ies, size_t ies_len, ++ const u8 *fils_session) ++{ ++ const u8 *ie, *end; ++ const u8 *session = NULL; ++ ++ if (!wpa_key_mgmt_fils(sm->wpa_key_mgmt)) { ++ wpa_printf(MSG_DEBUG, ++ "FILS: Not a FILS AKM - reject association"); ++ return NULL; ++ } ++ ++ /* Verify Session element */ ++ ie = ies; ++ end = ((const u8 *) ie) + ies_len; ++ while (ie + 1 < end) { ++ if (ie + 2 + ie[1] > end) ++ break; ++ if (ie[0] == WLAN_EID_EXTENSION && ++ ie[1] >= 1 + FILS_SESSION_LEN && ++ ie[2] == WLAN_EID_EXT_FILS_SESSION) { ++ session = ie; ++ break; ++ } ++ ie += 2 + ie[1]; ++ } ++ ++ if (!session) { ++ wpa_printf(MSG_DEBUG, ++ "FILS: %s: Could not find FILS Session element in Assoc Req - reject", ++ __func__); ++ return NULL; ++ } ++ ++ if (!fils_session) { ++ wpa_printf(MSG_DEBUG, ++ "FILS: %s: Could not find FILS Session element in STA entry - reject", ++ __func__); ++ return NULL; ++ } ++ ++ if (os_memcmp(fils_session, session + 3, FILS_SESSION_LEN) != 0) { ++ wpa_printf(MSG_DEBUG, "FILS: Session mismatch"); ++ wpa_hexdump(MSG_DEBUG, "FILS: Expected FILS Session", ++ fils_session, FILS_SESSION_LEN); ++ wpa_hexdump(MSG_DEBUG, "FILS: Received FILS Session", ++ session + 3, FILS_SESSION_LEN); ++ return NULL; ++ } ++ return session; ++} ++ ++ ++int wpa_fils_validate_key_confirm(struct wpa_state_machine *sm, const u8 *ies, ++ size_t ies_len) ++{ ++ struct ieee802_11_elems elems; ++ ++ if (ieee802_11_parse_elems(ies, ies_len, &elems, 1) == ParseFailed) { ++ wpa_printf(MSG_DEBUG, ++ "FILS: Failed to parse decrypted elements"); ++ return -1; ++ } ++ ++ if (!elems.fils_session) { ++ wpa_printf(MSG_DEBUG, "FILS: No FILS Session element"); ++ return -1; ++ } ++ ++ if (!elems.fils_key_confirm) { ++ wpa_printf(MSG_DEBUG, "FILS: No FILS Key Confirm element"); ++ return -1; ++ } ++ ++ if (elems.fils_key_confirm_len != sm->fils_key_auth_len) { ++ wpa_printf(MSG_DEBUG, ++ "FILS: Unexpected Key-Auth length %d (expected %d)", ++ elems.fils_key_confirm_len, ++ (int) sm->fils_key_auth_len); ++ return -1; ++ } ++ ++ if (os_memcmp(elems.fils_key_confirm, sm->fils_key_auth_sta, ++ sm->fils_key_auth_len) != 0) { ++ wpa_printf(MSG_DEBUG, "FILS: Key-Auth mismatch"); ++ wpa_hexdump(MSG_DEBUG, "FILS: Received Key-Auth", ++ elems.fils_key_confirm, elems.fils_key_confirm_len); ++ wpa_hexdump(MSG_DEBUG, "FILS: Expected Key-Auth", ++ sm->fils_key_auth_sta, sm->fils_key_auth_len); ++ return -1; ++ } ++ ++ return 0; ++} ++ ++ ++int fils_decrypt_assoc(struct wpa_state_machine *sm, const u8 *fils_session, ++ const struct ieee80211_mgmt *mgmt, size_t frame_len, ++ u8 *pos, size_t left) ++{ ++ u16 fc, stype; ++ const u8 *end, *ie_start, *ie, *session, *crypt; ++ const u8 *aad[5]; ++ size_t aad_len[5]; ++ ++ if (!sm || !sm->PTK_valid) { ++ wpa_printf(MSG_DEBUG, ++ "FILS: No KEK to decrypt Assocication Request frame"); ++ return -1; ++ } ++ ++ if (!wpa_key_mgmt_fils(sm->wpa_key_mgmt)) { ++ wpa_printf(MSG_DEBUG, ++ "FILS: Not a FILS AKM - reject association"); ++ return -1; ++ } ++ ++ end = ((const u8 *) mgmt) + frame_len; ++ fc = le_to_host16(mgmt->frame_control); ++ stype = WLAN_FC_GET_STYPE(fc); ++ if (stype == WLAN_FC_STYPE_REASSOC_REQ) ++ ie_start = mgmt->u.reassoc_req.variable; ++ else ++ ie_start = mgmt->u.assoc_req.variable; ++ ie = ie_start; ++ ++ /* ++ * Find FILS Session element which is the last unencrypted element in ++ * the frame. ++ */ ++ session = wpa_fils_validate_fils_session(sm, ie, end - ie, ++ fils_session); ++ if (!session) { ++ wpa_printf(MSG_DEBUG, "FILS: Session validation failed"); ++ return -1; ++ } ++ ++ crypt = session + 2 + session[1]; ++ ++ if (end - crypt < AES_BLOCK_SIZE) { ++ wpa_printf(MSG_DEBUG, ++ "FILS: Too short frame to include AES-SIV data"); ++ return -1; ++ } ++ ++ /* AES-SIV AAD vectors */ ++ ++ /* The STA's MAC address */ ++ aad[0] = mgmt->sa; ++ aad_len[0] = ETH_ALEN; ++ /* The AP's BSSID */ ++ aad[1] = mgmt->da; ++ aad_len[1] = ETH_ALEN; ++ /* The STA's nonce */ ++ aad[2] = sm->SNonce; ++ aad_len[2] = FILS_NONCE_LEN; ++ /* The AP's nonce */ ++ aad[3] = sm->ANonce; ++ aad_len[3] = FILS_NONCE_LEN; ++ /* ++ * The (Re)Association Request frame from the Capability Information ++ * field to the FILS Session element (both inclusive). ++ */ ++ aad[4] = (const u8 *) &mgmt->u.assoc_req.capab_info; ++ aad_len[4] = crypt - aad[4]; ++ ++ if (aes_siv_decrypt(sm->PTK.kek, sm->PTK.kek_len, crypt, end - crypt, ++ 5, aad, aad_len, pos + (crypt - ie_start)) < 0) { ++ wpa_printf(MSG_DEBUG, ++ "FILS: Invalid AES-SIV data in the frame"); ++ return -1; ++ } ++ wpa_hexdump(MSG_DEBUG, "FILS: Decrypted Association Request elements", ++ pos, left - AES_BLOCK_SIZE); ++ ++ if (wpa_fils_validate_key_confirm(sm, pos, left - AES_BLOCK_SIZE) < 0) { ++ wpa_printf(MSG_DEBUG, "FILS: Key Confirm validation failed"); ++ return -1; ++ } ++ ++ return left - AES_BLOCK_SIZE; ++} ++ ++ ++int fils_encrypt_assoc(struct wpa_state_machine *sm, u8 *buf, ++ size_t current_len, size_t max_len, ++ const struct wpabuf *hlp) ++{ ++ u8 *end = buf + max_len; ++ u8 *pos = buf + current_len; ++ struct ieee80211_mgmt *mgmt; ++ struct wpabuf *plain; ++ const u8 *aad[5]; ++ size_t aad_len[5]; ++ ++ if (!sm || !sm->PTK_valid) ++ return -1; ++ ++ wpa_hexdump(MSG_DEBUG, ++ "FILS: Association Response frame before FILS processing", ++ buf, current_len); ++ ++ mgmt = (struct ieee80211_mgmt *) buf; ++ ++ /* AES-SIV AAD vectors */ ++ ++ /* The AP's BSSID */ ++ aad[0] = mgmt->sa; ++ aad_len[0] = ETH_ALEN; ++ /* The STA's MAC address */ ++ aad[1] = mgmt->da; ++ aad_len[1] = ETH_ALEN; ++ /* The AP's nonce */ ++ aad[2] = sm->ANonce; ++ aad_len[2] = FILS_NONCE_LEN; ++ /* The STA's nonce */ ++ aad[3] = sm->SNonce; ++ aad_len[3] = FILS_NONCE_LEN; ++ /* ++ * The (Re)Association Response frame from the Capability Information ++ * field (the same offset in both Association and Reassociation ++ * Response frames) to the FILS Session element (both inclusive). ++ */ ++ aad[4] = (const u8 *) &mgmt->u.assoc_resp.capab_info; ++ aad_len[4] = pos - aad[4]; ++ ++ /* The following elements will be encrypted with AES-SIV */ ++ plain = fils_prepare_plainbuf(sm, hlp); ++ if (!plain) { ++ wpa_printf(MSG_DEBUG, "FILS: Plain buffer prep failed"); ++ return -1; ++ } ++ ++ if (pos + wpabuf_len(plain) + AES_BLOCK_SIZE > end) { ++ wpa_printf(MSG_DEBUG, ++ "FILS: Not enough room for FILS elements"); ++ wpabuf_free(plain); ++ return -1; ++ } ++ ++ wpa_hexdump_buf_key(MSG_DEBUG, "FILS: Association Response plaintext", ++ plain); ++ ++ if (aes_siv_encrypt(sm->PTK.kek, sm->PTK.kek_len, ++ wpabuf_head(plain), wpabuf_len(plain), ++ 5, aad, aad_len, pos) < 0) { ++ wpabuf_free(plain); ++ return -1; ++ } ++ ++ wpa_hexdump(MSG_DEBUG, ++ "FILS: Encrypted Association Response elements", ++ pos, AES_BLOCK_SIZE + wpabuf_len(plain)); ++ current_len += wpabuf_len(plain) + AES_BLOCK_SIZE; ++ wpabuf_free(plain); ++ ++ sm->fils_completed = 1; ++ ++ return current_len; ++} ++ ++ ++static struct wpabuf * fils_prepare_plainbuf(struct wpa_state_machine *sm, ++ const struct wpabuf *hlp) ++{ ++ struct wpabuf *plain; ++ u8 *len, *tmp, *tmp2; ++ u8 hdr[2]; ++ u8 *gtk, dummy_gtk[32]; ++ size_t gtk_len; ++ struct wpa_group *gsm; ++ ++ plain = wpabuf_alloc(1000); ++ if (!plain) ++ return NULL; ++ ++ /* TODO: FILS Public Key */ ++ ++ /* FILS Key Confirmation */ ++ wpabuf_put_u8(plain, WLAN_EID_EXTENSION); /* Element ID */ ++ wpabuf_put_u8(plain, 1 + sm->fils_key_auth_len); /* Length */ ++ /* Element ID Extension */ ++ wpabuf_put_u8(plain, WLAN_EID_EXT_FILS_KEY_CONFIRM); ++ wpabuf_put_data(plain, sm->fils_key_auth_ap, sm->fils_key_auth_len); ++ ++ /* FILS HLP Container */ ++ if (hlp) ++ wpabuf_put_buf(plain, hlp); ++ ++ /* TODO: FILS IP Address Assignment */ ++ ++ /* Key Delivery */ ++ gsm = sm->group; ++ wpabuf_put_u8(plain, WLAN_EID_EXTENSION); /* Element ID */ ++ len = wpabuf_put(plain, 1); ++ wpabuf_put_u8(plain, WLAN_EID_EXT_KEY_DELIVERY); ++ wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN, ++ wpabuf_put(plain, WPA_KEY_RSC_LEN)); ++ /* GTK KDE */ ++ gtk = gsm->GTK[gsm->GN - 1]; ++ gtk_len = gsm->GTK_len; ++ if (sm->wpa_auth->conf.disable_gtk || ++ sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN) { ++ /* ++ * Provide unique random GTK to each STA to prevent use ++ * of GTK in the BSS. ++ */ ++ if (random_get_bytes(dummy_gtk, gtk_len) < 0) { ++ wpabuf_free(plain); ++ return NULL; ++ } ++ gtk = dummy_gtk; ++ } ++ hdr[0] = gsm->GN & 0x03; ++ hdr[1] = 0; ++ tmp = wpabuf_put(plain, 0); ++ tmp2 = wpa_add_kde(tmp, RSN_KEY_DATA_GROUPKEY, hdr, 2, ++ gtk, gtk_len); ++ wpabuf_put(plain, tmp2 - tmp); ++ ++ /* IGTK KDE */ ++ tmp = wpabuf_put(plain, 0); ++ tmp2 = ieee80211w_kde_add(sm, tmp); ++ wpabuf_put(plain, tmp2 - tmp); ++ ++ *len = (u8 *) wpabuf_put(plain, 0) - len - 1; ++ ++#ifdef CONFIG_OCV ++ if (wpa_auth_uses_ocv(sm)) { ++ struct wpa_channel_info ci; ++ u8 *pos; ++ ++ if (wpa_channel_info(sm->wpa_auth, &ci) != 0) { ++ wpa_printf(MSG_WARNING, ++ "FILS: Failed to get channel info for OCI element"); ++ wpabuf_free(plain); ++ return NULL; ++ } ++ ++ pos = wpabuf_put(plain, OCV_OCI_EXTENDED_LEN); ++ if (ocv_insert_extended_oci(&ci, pos) < 0) { ++ wpabuf_free(plain); ++ return NULL; ++ } ++ } ++#endif /* CONFIG_OCV */ ++ ++ return plain; ++} ++ ++ ++int fils_set_tk(struct wpa_state_machine *sm) ++{ ++ enum wpa_alg alg; ++ int klen; ++ ++ if (!sm || !sm->PTK_valid) { ++ wpa_printf(MSG_DEBUG, "FILS: No valid PTK available to set TK"); ++ return -1; ++ } ++ if (sm->tk_already_set) { ++ wpa_printf(MSG_DEBUG, "FILS: TK already set to the driver"); ++ return -1; ++ } ++ ++ alg = wpa_cipher_to_alg(sm->pairwise); ++ klen = wpa_cipher_key_len(sm->pairwise); ++ ++ wpa_printf(MSG_DEBUG, "FILS: Configure TK to the driver"); ++ if (wpa_auth_set_key(sm->wpa_auth, 0, alg, sm->addr, 0, ++ sm->PTK.tk, klen)) { ++ wpa_printf(MSG_DEBUG, "FILS: Failed to set TK to the driver"); ++ return -1; ++ } ++ sm->tk_already_set = TRUE; ++ ++ return 0; ++} ++ ++ ++u8 * hostapd_eid_assoc_fils_session(struct wpa_state_machine *sm, u8 *buf, ++ const u8 *fils_session, struct wpabuf *hlp) ++{ ++ struct wpabuf *plain; ++ u8 *pos = buf; ++ ++ /* FILS Session */ ++ *pos++ = WLAN_EID_EXTENSION; /* Element ID */ ++ *pos++ = 1 + FILS_SESSION_LEN; /* Length */ ++ *pos++ = WLAN_EID_EXT_FILS_SESSION; /* Element ID Extension */ ++ os_memcpy(pos, fils_session, FILS_SESSION_LEN); ++ pos += FILS_SESSION_LEN; ++ ++ plain = fils_prepare_plainbuf(sm, hlp); ++ if (!plain) { ++ wpa_printf(MSG_DEBUG, "FILS: Plain buffer prep failed"); ++ return NULL; ++ } ++ ++ os_memcpy(pos, wpabuf_head(plain), wpabuf_len(plain)); ++ pos += wpabuf_len(plain); ++ ++ wpa_printf(MSG_DEBUG, "%s: plain buf_len: %u", __func__, ++ (unsigned int) wpabuf_len(plain)); ++ wpabuf_free(plain); ++ sm->fils_completed = 1; ++ return pos; ++} ++ ++#endif /* CONFIG_FILS */ ++ ++ ++#ifdef CONFIG_OCV ++int get_sta_tx_parameters(struct wpa_state_machine *sm, int ap_max_chanwidth, ++ int ap_seg1_idx, int *bandwidth, int *seg1_idx) ++{ ++ struct wpa_authenticator *wpa_auth = sm->wpa_auth; ++ ++ if (!wpa_auth->cb->get_sta_tx_params) ++ return -1; ++ return wpa_auth->cb->get_sta_tx_params(wpa_auth->cb_ctx, sm->addr, ++ ap_max_chanwidth, ap_seg1_idx, ++ bandwidth, seg1_idx); ++} ++#endif /* CONFIG_OCV */ ++ ++ + SM_STATE(WPA_PTK, PTKCALCNEGOTIATING) + { ++ struct wpa_authenticator *wpa_auth = sm->wpa_auth; + struct wpa_ptk PTK; + int ok = 0, psk_found = 0; + const u8 *pmk = NULL; ++ size_t pmk_len; ++ int ft; ++ const u8 *eapol_key_ie, *key_data, *mic; ++ u16 key_data_length; ++ size_t mic_len, eapol_key_ie_len; ++ struct ieee802_1x_hdr *hdr; ++ struct wpa_eapol_key *key; ++ struct wpa_eapol_ie_parse kde; ++ int vlan_id = 0; + + SM_ENTRY_MA(WPA_PTK, PTKCALCNEGOTIATING, wpa_ptk); + sm->EAPOLKeyReceived = FALSE; + sm->update_snonce = FALSE; ++ os_memset(&PTK, 0, sizeof(PTK)); + ++ mic_len = wpa_mic_len(sm->wpa_key_mgmt, sm->pmk_len); ++ + /* 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)) { ++ if (wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt) && ++ !wpa_key_mgmt_sae(sm->wpa_key_mgmt)) { + pmk = wpa_auth_get_psk(sm->wpa_auth, sm->addr, +- sm->p2p_dev_addr, pmk); ++ sm->p2p_dev_addr, pmk, &pmk_len, ++ &vlan_id); + if (pmk == NULL) + break; + psk_found = 1; +- } else ++#ifdef CONFIG_IEEE80211R_AP ++ if (wpa_key_mgmt_ft_psk(sm->wpa_key_mgmt)) { ++ os_memcpy(sm->xxkey, pmk, pmk_len); ++ sm->xxkey_len = pmk_len; ++ } ++#endif /* CONFIG_IEEE80211R_AP */ ++ } else { + pmk = sm->PMK; ++ pmk_len = sm->pmk_len; ++ } + +- wpa_derive_ptk(sm, sm->SNonce, pmk, &PTK); ++ if ((!pmk || !pmk_len) && sm->pmksa) { ++ wpa_printf(MSG_DEBUG, "WPA: Use PMK from PMKSA cache"); ++ pmk = sm->pmksa->pmk; ++ pmk_len = sm->pmksa->pmk_len; ++ } + +- if (wpa_verify_key_mic(sm->wpa_key_mgmt, &PTK, ++ if (wpa_derive_ptk(sm, sm->SNonce, pmk, pmk_len, &PTK) < 0) ++ break; ++ ++ if (mic_len && ++ wpa_verify_key_mic(sm->wpa_key_mgmt, pmk_len, &PTK, + sm->last_rx_eapol_key, + sm->last_rx_eapol_key_len) == 0) { ++ if (sm->PMK != pmk) { ++ os_memcpy(sm->PMK, pmk, pmk_len); ++ sm->pmk_len = pmk_len; ++ } + ok = 1; + break; + } + +- if (!wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt)) ++#ifdef CONFIG_FILS ++ if (!mic_len && ++ wpa_aead_decrypt(sm, &PTK, sm->last_rx_eapol_key, ++ sm->last_rx_eapol_key_len, NULL) == 0) { ++ ok = 1; + break; ++ } ++#endif /* CONFIG_FILS */ ++ ++ if (!wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt) || ++ wpa_key_mgmt_sae(sm->wpa_key_mgmt)) ++ break; + } + + if (!ok) { +@@ -2078,7 +2890,105 @@ + return; + } + +-#ifdef CONFIG_IEEE80211R ++ /* ++ * Note: last_rx_eapol_key length fields have already been validated in ++ * wpa_receive(). ++ */ ++ hdr = (struct ieee802_1x_hdr *) sm->last_rx_eapol_key; ++ key = (struct wpa_eapol_key *) (hdr + 1); ++ mic = (u8 *) (key + 1); ++ key_data = mic + mic_len + 2; ++ key_data_length = WPA_GET_BE16(mic + mic_len); ++ if (key_data_length > sm->last_rx_eapol_key_len - sizeof(*hdr) - ++ sizeof(*key) - mic_len - 2) ++ return; ++ ++ if (wpa_parse_kde_ies(key_data, key_data_length, &kde) < 0) { ++ wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_INFO, ++ "received EAPOL-Key msg 2/4 with invalid Key Data contents"); ++ return; ++ } ++ if (kde.rsn_ie) { ++ eapol_key_ie = kde.rsn_ie; ++ eapol_key_ie_len = kde.rsn_ie_len; ++ } else if (kde.osen) { ++ eapol_key_ie = kde.osen; ++ eapol_key_ie_len = kde.osen_len; ++ } else { ++ eapol_key_ie = kde.wpa_ie; ++ eapol_key_ie_len = kde.wpa_ie_len; ++ } ++ ft = sm->wpa == WPA_VERSION_WPA2 && wpa_key_mgmt_ft(sm->wpa_key_mgmt); ++ if (sm->wpa_ie == NULL || ++ wpa_compare_rsn_ie(ft, sm->wpa_ie, sm->wpa_ie_len, ++ eapol_key_ie, eapol_key_ie_len)) { ++ wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO, ++ "WPA IE from (Re)AssocReq did not match with msg 2/4"); ++ if (sm->wpa_ie) { ++ wpa_hexdump(MSG_DEBUG, "WPA IE in AssocReq", ++ sm->wpa_ie, sm->wpa_ie_len); ++ } ++ wpa_hexdump(MSG_DEBUG, "WPA IE in msg 2/4", ++ eapol_key_ie, eapol_key_ie_len); ++ /* MLME-DEAUTHENTICATE.request */ ++ wpa_sta_disconnect(wpa_auth, sm->addr, ++ WLAN_REASON_PREV_AUTH_NOT_VALID); ++ return; ++ } ++#ifdef CONFIG_OCV ++ if (wpa_auth_uses_ocv(sm)) { ++ struct wpa_channel_info ci; ++ int tx_chanwidth; ++ int tx_seg1_idx; ++ ++ if (wpa_channel_info(wpa_auth, &ci) != 0) { ++ wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO, ++ "Failed to get channel info to validate received OCI in EAPOL-Key 2/4"); ++ return; ++ } ++ ++ if (get_sta_tx_parameters(sm, ++ channel_width_to_int(ci.chanwidth), ++ ci.seg1_idx, &tx_chanwidth, ++ &tx_seg1_idx) < 0) ++ return; ++ ++ if (ocv_verify_tx_params(kde.oci, kde.oci_len, &ci, ++ tx_chanwidth, tx_seg1_idx) != 0) { ++ wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO, ++ ocv_errorstr); ++ return; ++ } ++ } ++#endif /* CONFIG_OCV */ ++#ifdef CONFIG_IEEE80211R_AP ++ if (ft && ft_check_msg_2_of_4(wpa_auth, sm, &kde) < 0) { ++ wpa_sta_disconnect(wpa_auth, sm->addr, ++ WLAN_REASON_PREV_AUTH_NOT_VALID); ++ return; ++ } ++#endif /* CONFIG_IEEE80211R_AP */ ++#ifdef CONFIG_P2P ++ if (kde.ip_addr_req && kde.ip_addr_req[0] && ++ wpa_auth->ip_pool && WPA_GET_BE32(sm->ip_addr) == 0) { ++ int idx; ++ wpa_printf(MSG_DEBUG, ++ "P2P: IP address requested in EAPOL-Key exchange"); ++ idx = bitfield_get_first_zero(wpa_auth->ip_pool); ++ if (idx >= 0) { ++ u32 start = WPA_GET_BE32(wpa_auth->conf.ip_addr_start); ++ bitfield_set(wpa_auth->ip_pool, idx); ++ WPA_PUT_BE32(sm->ip_addr, start + idx); ++ wpa_printf(MSG_DEBUG, ++ "P2P: Assigned IP address %u.%u.%u.%u to " ++ MACSTR, sm->ip_addr[0], sm->ip_addr[1], ++ sm->ip_addr[2], sm->ip_addr[3], ++ MAC2STR(sm->addr)); ++ } ++ } ++#endif /* CONFIG_P2P */ ++ ++#ifdef CONFIG_IEEE80211R_AP + if (sm->wpa == WPA_VERSION_WPA2 && wpa_key_mgmt_ft(sm->wpa_key_mgmt)) { + /* + * Verify that PMKR1Name from EAPOL-Key message 2/4 matches +@@ -2097,8 +3007,15 @@ + return; + } + } +-#endif /* CONFIG_IEEE80211R */ ++#endif /* CONFIG_IEEE80211R_AP */ + ++ if (vlan_id && wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt) && ++ wpa_auth_update_vlan(wpa_auth, sm->addr, vlan_id) < 0) { ++ wpa_sta_disconnect(wpa_auth, sm->addr, ++ WLAN_REASON_PREV_AUTH_NOT_VALID); ++ return; ++ } ++ + sm->pending_1_of_4_timeout = 0; + eloop_cancel_timeout(wpa_send_eapol_timeout, sm->wpa_auth, sm); + +@@ -2107,6 +3024,7 @@ + * state machine data based on whatever PSK was selected here. + */ + os_memcpy(sm->PMK, pmk, PMK_LEN); ++ sm->pmk_len = PMK_LEN; + } + + sm->MICVerified = TRUE; +@@ -2155,7 +3073,8 @@ + else + os_memcpy(igtk.pn, rsc, sizeof(igtk.pn)); + os_memcpy(igtk.igtk, gsm->IGTK[gsm->GN_igtk - 4], len); +- if (sm->wpa_auth->conf.disable_gtk) { ++ if (sm->wpa_auth->conf.disable_gtk || ++ sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN) { + /* + * Provide unique random IGTK to each STA to prevent use of + * IGTK in the BSS. +@@ -2186,6 +3105,36 @@ + #endif /* CONFIG_IEEE80211W */ + + ++static int ocv_oci_len(struct wpa_state_machine *sm) ++{ ++#ifdef CONFIG_OCV ++ if (wpa_auth_uses_ocv(sm)) ++ return OCV_OCI_KDE_LEN; ++#endif /* CONFIG_OCV */ ++ return 0; ++} ++ ++static int ocv_oci_add(struct wpa_state_machine *sm, u8 **argpos) ++{ ++#ifdef CONFIG_OCV ++ struct wpa_channel_info ci; ++ ++ if (!wpa_auth_uses_ocv(sm)) ++ return 0; ++ ++ if (wpa_channel_info(sm->wpa_auth, &ci) != 0) { ++ wpa_printf(MSG_WARNING, ++ "Failed to get channel info for OCI element"); ++ return -1; ++ } ++ ++ return ocv_insert_oci_kde(&ci, argpos); ++#else /* CONFIG_OCV */ ++ return 0; ++#endif /* CONFIG_OCV */ ++} ++ ++ + SM_STATE(WPA_PTK, PTKINITNEGOTIATING) + { + u8 rsc[WPA_KEY_RSC_LEN], *_rsc, *gtk, *kde, *pos, dummy_gtk[32]; +@@ -2198,7 +3147,12 @@ + sm->TimeoutEvt = FALSE; + + sm->TimeoutCtr++; +- if (sm->TimeoutCtr > (int) dot11RSNAConfigPairwiseUpdateCount) { ++ if (sm->wpa_auth->conf.wpa_disable_eapol_key_retries && ++ sm->TimeoutCtr > 1) { ++ /* Do not allow retransmission of EAPOL-Key msg 3/4 */ ++ return; ++ } ++ if (sm->TimeoutCtr > sm->wpa_auth->conf.wpa_pairwise_update_count) { + /* No point in sending the EAPOL-Key - we will disconnect + * immediately following this. */ + return; +@@ -2228,7 +3182,8 @@ + secure = 1; + gtk = gsm->GTK[gsm->GN - 1]; + gtk_len = gsm->GTK_len; +- if (sm->wpa_auth->conf.disable_gtk) { ++ if (sm->wpa_auth->conf.disable_gtk || ++ sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN) { + /* + * Provide unique random GTK to each STA to prevent use + * of GTK in the BSS. +@@ -2263,15 +3218,15 @@ + } + } + +- kde_len = wpa_ie_len + ieee80211w_kde_len(sm); ++ kde_len = wpa_ie_len + ieee80211w_kde_len(sm) + ocv_oci_len(sm); + if (gtk) + kde_len += 2 + RSN_SELECTOR_LEN + 2 + gtk_len; +-#ifdef CONFIG_IEEE80211R ++#ifdef CONFIG_IEEE80211R_AP + 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 */ ++#endif /* CONFIG_IEEE80211R_AP */ + #ifdef CONFIG_P2P + if (WPA_GET_BE32(sm->ip_addr) > 0) + kde_len += 2 + RSN_SELECTOR_LEN + 3 * 4; +@@ -2283,9 +3238,13 @@ + pos = kde; + os_memcpy(pos, wpa_ie, wpa_ie_len); + pos += wpa_ie_len; +-#ifdef CONFIG_IEEE80211R ++#ifdef CONFIG_IEEE80211R_AP + if (wpa_key_mgmt_ft(sm->wpa_key_mgmt)) { +- int res = wpa_insert_pmkid(kde, pos - kde, sm->pmk_r1_name); ++ int res; ++ size_t elen; ++ ++ elen = pos - kde; ++ res = wpa_insert_pmkid(kde, &elen, sm->pmk_r1_name); + if (res < 0) { + wpa_printf(MSG_ERROR, "FT: Failed to insert " + "PMKR1Name into RSN IE in EAPOL-Key data"); +@@ -2292,9 +3251,10 @@ + os_free(kde); + return; + } +- pos += res; ++ pos -= wpa_ie_len; ++ pos += elen; + } +-#endif /* CONFIG_IEEE80211R */ ++#endif /* CONFIG_IEEE80211R_AP */ + if (gtk) { + u8 hdr[2]; + hdr[0] = keyidx & 0x03; +@@ -2303,17 +3263,32 @@ + gtk, gtk_len); + } + pos = ieee80211w_kde_add(sm, pos); ++ if (ocv_oci_add(sm, &pos) < 0) { ++ os_free(kde); ++ return; ++ } + +-#ifdef CONFIG_IEEE80211R ++#ifdef CONFIG_IEEE80211R_AP + 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 (sm->assoc_resp_ftie && ++ kde + kde_len - pos >= 2 + sm->assoc_resp_ftie[1]) { ++ os_memcpy(pos, sm->assoc_resp_ftie, ++ 2 + sm->assoc_resp_ftie[1]); ++ res = 2 + sm->assoc_resp_ftie[1]; ++ } else { ++ int use_sha384 = wpa_key_mgmt_sha384(sm->wpa_key_mgmt); ++ ++ res = wpa_write_ftie(conf, use_sha384, ++ 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"); +@@ -2333,10 +3308,10 @@ + *pos++ = WLAN_EID_TIMEOUT_INTERVAL; + *pos++ = 5; + *pos++ = WLAN_TIMEOUT_KEY_LIFETIME; +- WPA_PUT_LE32(pos, conf->r0_key_lifetime * 60); ++ WPA_PUT_LE32(pos, conf->r0_key_lifetime); + pos += 4; + } +-#endif /* CONFIG_IEEE80211R */ ++#endif /* CONFIG_IEEE80211R_AP */ + #ifdef CONFIG_P2P + if (WPA_GET_BE32(sm->ip_addr) > 0) { + u8 addr[3 * 4]; +@@ -2349,7 +3324,9 @@ + #endif /* CONFIG_P2P */ + + wpa_send_eapol(sm->wpa_auth, sm, +- (secure ? WPA_KEY_INFO_SECURE : 0) | WPA_KEY_INFO_MIC | ++ (secure ? WPA_KEY_INFO_SECURE : 0) | ++ (wpa_mic_len(sm->wpa_key_mgmt, sm->pmk_len) ? ++ WPA_KEY_INFO_MIC : 0) | + WPA_KEY_INFO_ACK | WPA_KEY_INFO_INSTALL | + WPA_KEY_INFO_KEY_TYPE, + _rsc, sm->ANonce, kde, pos - kde, keyidx, encr); +@@ -2366,20 +3343,18 @@ + int klen = wpa_cipher_key_len(sm->pairwise); + if (wpa_auth_set_key(sm->wpa_auth, 0, alg, sm->addr, 0, + sm->PTK.tk, klen)) { +- wpa_sta_disconnect(sm->wpa_auth, sm->addr); ++ wpa_sta_disconnect(sm->wpa_auth, sm->addr, ++ WLAN_REASON_PREV_AUTH_NOT_VALID); + 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); +- } ++ wpa_auth_set_ptk_rekey_timer(sm); + +- if (wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt)) { ++ if (wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt) || ++ sm->wpa_key_mgmt == WPA_KEY_MGMT_DPP || ++ sm->wpa_key_mgmt == WPA_KEY_MGMT_OWE) { + wpa_auth_set_eapol(sm->wpa_auth, sm->addr, + WPA_EAPOL_authorized, 1); + } +@@ -2405,9 +3380,9 @@ + "pairwise key handshake completed (%s)", + sm->wpa == WPA_VERSION_WPA ? "WPA" : "RSN"); + +-#ifdef CONFIG_IEEE80211R ++#ifdef CONFIG_IEEE80211R_AP + wpa_ft_push_pmk_r1(sm->wpa_auth, sm->addr); +-#endif /* CONFIG_IEEE80211R */ ++#endif /* CONFIG_IEEE80211R_AP */ + } + + +@@ -2451,15 +3426,22 @@ + 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) ++ else if (wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt) || ++ sm->wpa_key_mgmt == WPA_KEY_MGMT_OWE + /* FIX: && 802.1X::keyRun */) + SM_ENTER(WPA_PTK, INITPSK); ++ else if (sm->wpa_key_mgmt == WPA_KEY_MGMT_DPP) ++ SM_ENTER(WPA_PTK, INITPMK); + break; + case WPA_PTK_INITPMK: + if (wpa_auth_get_eapol(sm->wpa_auth, sm->addr, +- WPA_EAPOL_keyAvailable) > 0) ++ WPA_EAPOL_keyAvailable) > 0) { + SM_ENTER(WPA_PTK, PTKSTART); +- else { ++#ifdef CONFIG_DPP ++ } else if (sm->wpa_key_mgmt == WPA_KEY_MGMT_DPP && sm->pmksa) { ++ SM_ENTER(WPA_PTK, PTKSTART); ++#endif /* CONFIG_DPP */ ++ } else { + wpa_auth->dot11RSNA4WayHandshakeFailures++; + wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_INFO, + "INITPMK - keyAvailable = false"); +@@ -2468,9 +3450,13 @@ + break; + case WPA_PTK_INITPSK: + if (wpa_auth_get_psk(sm->wpa_auth, sm->addr, sm->p2p_dev_addr, +- NULL)) ++ NULL, NULL, NULL)) { + SM_ENTER(WPA_PTK, PTKSTART); +- else { ++#ifdef CONFIG_SAE ++ } else if (wpa_auth_uses_sae(sm) && sm->pmksa) { ++ SM_ENTER(WPA_PTK, PTKSTART); ++#endif /* CONFIG_SAE */ ++ } else { + wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_INFO, + "no PSK configured for the STA"); + wpa_auth->dot11RSNA4WayHandshakeFailures++; +@@ -2482,11 +3468,12 @@ + sm->EAPOLKeyPairwise) + SM_ENTER(WPA_PTK, PTKCALCNEGOTIATING); + else if (sm->TimeoutCtr > +- (int) dot11RSNAConfigPairwiseUpdateCount) { ++ sm->wpa_auth->conf.wpa_pairwise_update_count) { + wpa_auth->dot11RSNA4WayHandshakeFailures++; +- wpa_auth_vlogger(sm->wpa_auth, sm->addr, LOGGER_DEBUG, +- "PTKSTART: Retry limit %d reached", +- dot11RSNAConfigPairwiseUpdateCount); ++ wpa_auth_vlogger( ++ sm->wpa_auth, sm->addr, LOGGER_DEBUG, ++ "PTKSTART: Retry limit %u reached", ++ sm->wpa_auth->conf.wpa_pairwise_update_count); + SM_ENTER(WPA_PTK, DISCONNECT); + } else if (sm->TimeoutEvt) + SM_ENTER(WPA_PTK, PTKSTART); +@@ -2510,12 +3497,14 @@ + sm->EAPOLKeyPairwise && sm->MICVerified) + SM_ENTER(WPA_PTK, PTKINITDONE); + else if (sm->TimeoutCtr > +- (int) dot11RSNAConfigPairwiseUpdateCount) { ++ sm->wpa_auth->conf.wpa_pairwise_update_count || ++ (sm->wpa_auth->conf.wpa_disable_eapol_key_retries && ++ sm->TimeoutCtr > 1)) { + wpa_auth->dot11RSNA4WayHandshakeFailures++; +- wpa_auth_vlogger(sm->wpa_auth, sm->addr, LOGGER_DEBUG, +- "PTKINITNEGOTIATING: Retry limit %d " +- "reached", +- dot11RSNAConfigPairwiseUpdateCount); ++ wpa_auth_vlogger( ++ sm->wpa_auth, sm->addr, LOGGER_DEBUG, ++ "PTKINITNEGOTIATING: Retry limit %u reached", ++ sm->wpa_auth->conf.wpa_pairwise_update_count); + SM_ENTER(WPA_PTK, DISCONNECT); + } else if (sm->TimeoutEvt) + SM_ENTER(WPA_PTK, PTKINITNEGOTIATING); +@@ -2550,7 +3539,12 @@ + SM_ENTRY_MA(WPA_PTK_GROUP, REKEYNEGOTIATING, wpa_ptk_group); + + sm->GTimeoutCtr++; +- if (sm->GTimeoutCtr > (int) dot11RSNAConfigGroupUpdateCount) { ++ if (sm->wpa_auth->conf.wpa_disable_eapol_key_retries && ++ sm->GTimeoutCtr > 1) { ++ /* Do not allow retransmission of EAPOL-Key group msg 1/2 */ ++ return; ++ } ++ if (sm->GTimeoutCtr > sm->wpa_auth->conf.wpa_group_update_count) { + /* No point in sending the EAPOL-Key - we will disconnect + * immediately following this. */ + return; +@@ -2567,7 +3561,8 @@ + "sending 1/2 msg of Group Key Handshake"); + + gtk = gsm->GTK[gsm->GN - 1]; +- if (sm->wpa_auth->conf.disable_gtk) { ++ if (sm->wpa_auth->conf.disable_gtk || ++ sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN) { + /* + * Provide unique random GTK to each STA to prevent use + * of GTK in the BSS. +@@ -2578,7 +3573,7 @@ + } + if (sm->wpa == WPA_VERSION_WPA2) { + kde_len = 2 + RSN_SELECTOR_LEN + 2 + gsm->GTK_len + +- ieee80211w_kde_len(sm); ++ ieee80211w_kde_len(sm) + ocv_oci_len(sm); + kde_buf = os_malloc(kde_len); + if (kde_buf == NULL) + return; +@@ -2589,6 +3584,10 @@ + pos = wpa_add_kde(pos, RSN_KEY_DATA_GROUPKEY, hdr, 2, + gtk, gsm->GTK_len); + pos = ieee80211w_kde_add(sm, pos); ++ if (ocv_oci_add(sm, &pos) < 0) { ++ os_free(kde_buf); ++ return; ++ } + kde_len = pos - kde; + } else { + kde = gtk; +@@ -2596,10 +3595,12 @@ + } + + wpa_send_eapol(sm->wpa_auth, sm, +- WPA_KEY_INFO_SECURE | WPA_KEY_INFO_MIC | ++ WPA_KEY_INFO_SECURE | ++ (wpa_mic_len(sm->wpa_key_mgmt, sm->pmk_len) ? ++ WPA_KEY_INFO_MIC : 0) | + WPA_KEY_INFO_ACK | + (!sm->Pair ? WPA_KEY_INFO_INSTALL : 0), +- rsc, gsm->GNonce, kde, kde_len, gsm->GN, 1); ++ rsc, NULL, kde, kde_len, gsm->GN, 1); + + os_free(kde_buf); + } +@@ -2607,8 +3608,67 @@ + + SM_STATE(WPA_PTK_GROUP, REKEYESTABLISHED) + { ++#ifdef CONFIG_OCV ++ struct wpa_authenticator *wpa_auth = sm->wpa_auth; ++ const u8 *key_data, *mic; ++ struct ieee802_1x_hdr *hdr; ++ struct wpa_eapol_key *key; ++ struct wpa_eapol_ie_parse kde; ++ size_t mic_len; ++ u16 key_data_length; ++#endif /* CONFIG_OCV */ ++ + SM_ENTRY_MA(WPA_PTK_GROUP, REKEYESTABLISHED, wpa_ptk_group); + sm->EAPOLKeyReceived = FALSE; ++ ++#ifdef CONFIG_OCV ++ mic_len = wpa_mic_len(sm->wpa_key_mgmt, sm->pmk_len); ++ ++ /* ++ * Note: last_rx_eapol_key length fields have already been validated in ++ * wpa_receive(). ++ */ ++ hdr = (struct ieee802_1x_hdr *) sm->last_rx_eapol_key; ++ key = (struct wpa_eapol_key *) (hdr + 1); ++ mic = (u8 *) (key + 1); ++ key_data = mic + mic_len + 2; ++ key_data_length = WPA_GET_BE16(mic + mic_len); ++ if (key_data_length > sm->last_rx_eapol_key_len - sizeof(*hdr) - ++ sizeof(*key) - mic_len - 2) ++ return; ++ ++ if (wpa_parse_kde_ies(key_data, key_data_length, &kde) < 0) { ++ wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_INFO, ++ "received EAPOL-Key group msg 2/2 with invalid Key Data contents"); ++ return; ++ } ++ ++ if (wpa_auth_uses_ocv(sm)) { ++ struct wpa_channel_info ci; ++ int tx_chanwidth; ++ int tx_seg1_idx; ++ ++ if (wpa_channel_info(wpa_auth, &ci) != 0) { ++ wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO, ++ "Failed to get channel info to validate received OCI in EAPOL-Key group 1/2"); ++ return; ++ } ++ ++ if (get_sta_tx_parameters(sm, ++ channel_width_to_int(ci.chanwidth), ++ ci.seg1_idx, &tx_chanwidth, ++ &tx_seg1_idx) < 0) ++ return; ++ ++ if (ocv_verify_tx_params(kde.oci, kde.oci_len, &ci, ++ tx_chanwidth, tx_seg1_idx) != 0) { ++ wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO, ++ ocv_errorstr); ++ return; ++ } ++ } ++#endif /* CONFIG_OCV */ ++ + if (sm->GUpdateStationKeys) + sm->group->GKeyDoneStations--; + sm->GUpdateStationKeys = FALSE; +@@ -2628,6 +3688,10 @@ + sm->group->GKeyDoneStations--; + sm->GUpdateStationKeys = FALSE; + sm->Disconnect = TRUE; ++ wpa_auth_vlogger(sm->wpa_auth, sm->addr, LOGGER_INFO, ++ "group key handshake failed (%s) after %u tries", ++ sm->wpa == WPA_VERSION_WPA ? "WPA" : "RSN", ++ sm->wpa_auth->conf.wpa_group_update_count); + } + + +@@ -2647,7 +3711,9 @@ + !sm->EAPOLKeyPairwise && sm->MICVerified) + SM_ENTER(WPA_PTK_GROUP, REKEYESTABLISHED); + else if (sm->GTimeoutCtr > +- (int) dot11RSNAConfigGroupUpdateCount) ++ sm->wpa_auth->conf.wpa_group_update_count || ++ (sm->wpa_auth->conf.wpa_disable_eapol_key_retries && ++ sm->GTimeoutCtr > 1)) + SM_ENTER(WPA_PTK_GROUP, KEYERROR); + else if (sm->TimeoutEvt) + SM_ENTER(WPA_PTK_GROUP, REKEYNEGOTIATING); +@@ -2750,7 +3816,7 @@ + } + + +-#ifdef CONFIG_WNM ++#ifdef CONFIG_WNM_AP + /* update GTK when exiting WNM-Sleep Mode */ + void wpa_wnmsleep_rekey_gtk(struct wpa_state_machine *sm) + { +@@ -2829,7 +3895,7 @@ + return pos - start; + } + #endif /* CONFIG_IEEE80211W */ +-#endif /* CONFIG_WNM */ ++#endif /* CONFIG_WNM_AP */ + + + static void wpa_group_setkeys(struct wpa_authenticator *wpa_auth, +@@ -3107,8 +4173,8 @@ + "dot11RSNAConfigNumberOfGTKSAReplayCounters=0\n", + RSN_VERSION, + !!wpa_auth->conf.wpa_strict_rekey, +- dot11RSNAConfigGroupUpdateCount, +- dot11RSNAConfigPairwiseUpdateCount, ++ wpa_auth->conf.wpa_group_update_count, ++ wpa_auth->conf.wpa_pairwise_update_count, + wpa_cipher_key_len(wpa_auth->conf.wpa_group) * 8, + dot11RSNAConfigPMKLifetime, + dot11RSNAConfigPMKReauthThreshold, +@@ -3211,6 +4277,15 @@ + } + + ++const u8 * wpa_auth_get_pmk(struct wpa_state_machine *sm, int *len) ++{ ++ if (!sm) ++ return NULL; ++ *len = sm->pmk_len; ++ return sm->PMK; ++} ++ ++ + int wpa_auth_sta_key_mgmt(struct wpa_state_machine *sm) + { + if (sm == NULL) +@@ -3235,6 +4310,14 @@ + } + + ++int wpa_auth_sta_fils_tk_already_set(struct wpa_state_machine *sm) ++{ ++ if (!sm || !wpa_key_mgmt_fils(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) + { +@@ -3269,6 +4352,7 @@ + + + int wpa_auth_pmksa_add(struct wpa_state_machine *sm, const u8 *pmk, ++ unsigned int pmk_len, + int session_timeout, struct eapol_state_machine *eapol) + { + if (sm == NULL || sm->wpa != WPA_VERSION_WPA2 || +@@ -3275,7 +4359,14 @@ + sm->wpa_auth->conf.disable_pmksa_caching) + return -1; + +- if (pmksa_cache_auth_add(sm->wpa_auth->pmksa, pmk, PMK_LEN, ++ if (wpa_key_mgmt_sha384(sm->wpa_key_mgmt)) { ++ if (pmk_len > PMK_LEN_SUITE_B_192) ++ pmk_len = PMK_LEN_SUITE_B_192; ++ } else if (pmk_len > PMK_LEN) { ++ pmk_len = PMK_LEN; ++ } ++ ++ if (pmksa_cache_auth_add(sm->wpa_auth->pmksa, pmk, pmk_len, NULL, + sm->PTK.kck, sm->PTK.kck_len, + sm->wpa_auth->addr, sm->addr, session_timeout, + eapol, sm->wpa_key_mgmt)) +@@ -3293,7 +4384,7 @@ + if (wpa_auth == NULL) + return -1; + +- if (pmksa_cache_auth_add(wpa_auth->pmksa, pmk, len, ++ if (pmksa_cache_auth_add(wpa_auth->pmksa, pmk, len, NULL, + NULL, 0, + wpa_auth->addr, + sta_addr, session_timeout, eapol, +@@ -3305,12 +4396,12 @@ + + + int wpa_auth_pmksa_add_sae(struct wpa_authenticator *wpa_auth, const u8 *addr, +- const u8 *pmk) ++ const u8 *pmk, const u8 *pmkid) + { + if (wpa_auth->conf.disable_pmksa_caching) + return -1; + +- if (pmksa_cache_auth_add(wpa_auth->pmksa, pmk, PMK_LEN, ++ if (pmksa_cache_auth_add(wpa_auth->pmksa, pmk, PMK_LEN, pmkid, + NULL, 0, + wpa_auth->addr, addr, 0, NULL, + WPA_KEY_MGMT_SAE)) +@@ -3320,6 +4411,29 @@ + } + + ++void wpa_auth_add_sae_pmkid(struct wpa_state_machine *sm, const u8 *pmkid) ++{ ++ os_memcpy(sm->pmkid, pmkid, PMKID_LEN); ++ sm->pmkid_set = 1; ++} ++ ++ ++int wpa_auth_pmksa_add2(struct wpa_authenticator *wpa_auth, const u8 *addr, ++ const u8 *pmk, size_t pmk_len, const u8 *pmkid, ++ int session_timeout, int akmp) ++{ ++ if (wpa_auth->conf.disable_pmksa_caching) ++ return -1; ++ ++ if (pmksa_cache_auth_add(wpa_auth->pmksa, pmk, pmk_len, pmkid, ++ NULL, 0, wpa_auth->addr, addr, session_timeout, ++ NULL, akmp)) ++ return 0; ++ ++ return -1; ++} ++ ++ + void wpa_auth_pmksa_remove(struct wpa_authenticator *wpa_auth, + const u8 *sta_addr) + { +@@ -3336,6 +4450,99 @@ + } + + ++int wpa_auth_pmksa_list(struct wpa_authenticator *wpa_auth, char *buf, ++ size_t len) ++{ ++ if (!wpa_auth || !wpa_auth->pmksa) ++ return 0; ++ return pmksa_cache_auth_list(wpa_auth->pmksa, buf, len); ++} ++ ++ ++void wpa_auth_pmksa_flush(struct wpa_authenticator *wpa_auth) ++{ ++ if (wpa_auth && wpa_auth->pmksa) ++ pmksa_cache_auth_flush(wpa_auth->pmksa); ++} ++ ++ ++#ifdef CONFIG_PMKSA_CACHE_EXTERNAL ++#ifdef CONFIG_MESH ++ ++int wpa_auth_pmksa_list_mesh(struct wpa_authenticator *wpa_auth, const u8 *addr, ++ char *buf, size_t len) ++{ ++ if (!wpa_auth || !wpa_auth->pmksa) ++ return 0; ++ ++ return pmksa_cache_auth_list_mesh(wpa_auth->pmksa, addr, buf, len); ++} ++ ++ ++struct rsn_pmksa_cache_entry * ++wpa_auth_pmksa_create_entry(const u8 *aa, const u8 *spa, const u8 *pmk, ++ const u8 *pmkid, int expiration) ++{ ++ struct rsn_pmksa_cache_entry *entry; ++ struct os_reltime now; ++ ++ entry = pmksa_cache_auth_create_entry(pmk, PMK_LEN, pmkid, NULL, 0, aa, ++ spa, 0, NULL, WPA_KEY_MGMT_SAE); ++ if (!entry) ++ return NULL; ++ ++ os_get_reltime(&now); ++ entry->expiration = now.sec + expiration; ++ return entry; ++} ++ ++ ++int wpa_auth_pmksa_add_entry(struct wpa_authenticator *wpa_auth, ++ struct rsn_pmksa_cache_entry *entry) ++{ ++ int ret; ++ ++ if (!wpa_auth || !wpa_auth->pmksa) ++ return -1; ++ ++ ret = pmksa_cache_auth_add_entry(wpa_auth->pmksa, entry); ++ if (ret < 0) ++ wpa_printf(MSG_DEBUG, ++ "RSN: Failed to store external PMKSA cache for " ++ MACSTR, MAC2STR(entry->spa)); ++ ++ return ret; ++} ++ ++#endif /* CONFIG_MESH */ ++#endif /* CONFIG_PMKSA_CACHE_EXTERNAL */ ++ ++ ++struct rsn_pmksa_cache_entry * ++wpa_auth_pmksa_get(struct wpa_authenticator *wpa_auth, const u8 *sta_addr, ++ const u8 *pmkid) ++{ ++ if (!wpa_auth || !wpa_auth->pmksa) ++ return NULL; ++ return pmksa_cache_auth_get(wpa_auth->pmksa, sta_addr, pmkid); ++} ++ ++ ++void wpa_auth_pmksa_set_to_sm(struct rsn_pmksa_cache_entry *pmksa, ++ struct wpa_state_machine *sm, ++ struct wpa_authenticator *wpa_auth, ++ u8 *pmkid, u8 *pmk) ++{ ++ if (!sm) ++ return; ++ ++ sm->pmksa = pmksa; ++ os_memcpy(pmk, pmksa->pmk, PMK_LEN); ++ os_memcpy(pmkid, pmksa->pmkid, PMKID_LEN); ++ os_memcpy(wpa_auth->dot11RSNAPMKIDUsed, pmksa->pmkid, PMKID_LEN); ++} ++ ++ + /* + * Remove and free the group from wpa_authenticator. This is triggered by a + * callback to make sure nobody is currently iterating the group list while it +@@ -3414,6 +4621,98 @@ + } + + ++/* ++ * Enforce that the group state machine for the VLAN is running, increase ++ * reference counter as interface is up. References might have been increased ++ * even if a negative value is returned. ++ * Returns: -1 on error (group missing, group already failed); otherwise, 0 ++ */ ++int wpa_auth_ensure_group(struct wpa_authenticator *wpa_auth, int vlan_id) ++{ ++ struct wpa_group *group; ++ ++ if (wpa_auth == NULL) ++ return 0; ++ ++ group = wpa_auth->group; ++ while (group) { ++ if (group->vlan_id == vlan_id) ++ break; ++ group = group->next; ++ } ++ ++ if (group == NULL) { ++ group = wpa_auth_add_group(wpa_auth, vlan_id); ++ if (group == NULL) ++ return -1; ++ } ++ ++ wpa_printf(MSG_DEBUG, ++ "WPA: Ensure group state machine running for VLAN ID %d", ++ vlan_id); ++ ++ wpa_group_get(wpa_auth, group); ++ group->num_setup_iface++; ++ ++ if (group->wpa_group_state == WPA_GROUP_FATAL_FAILURE) ++ return -1; ++ ++ return 0; ++} ++ ++ ++/* ++ * Decrease reference counter, expected to be zero afterwards. ++ * returns: -1 on error (group not found, group in fail state) ++ * -2 if wpa_group is still referenced ++ * 0 else ++ */ ++int wpa_auth_release_group(struct wpa_authenticator *wpa_auth, int vlan_id) ++{ ++ struct wpa_group *group; ++ int ret = 0; ++ ++ if (wpa_auth == NULL) ++ return 0; ++ ++ group = wpa_auth->group; ++ while (group) { ++ if (group->vlan_id == vlan_id) ++ break; ++ group = group->next; ++ } ++ ++ if (group == NULL) ++ return -1; ++ ++ wpa_printf(MSG_DEBUG, ++ "WPA: Try stopping group state machine for VLAN ID %d", ++ vlan_id); ++ ++ if (group->num_setup_iface <= 0) { ++ wpa_printf(MSG_ERROR, ++ "WPA: wpa_auth_release_group called more often than wpa_auth_ensure_group for VLAN ID %d, skipping.", ++ vlan_id); ++ return -1; ++ } ++ group->num_setup_iface--; ++ ++ if (group->wpa_group_state == WPA_GROUP_FATAL_FAILURE) ++ ret = -1; ++ ++ if (group->references > 1) { ++ wpa_printf(MSG_DEBUG, ++ "WPA: Cannot stop group state machine for VLAN ID %d as references are still hold", ++ vlan_id); ++ ret = -2; ++ } ++ ++ wpa_group_put(wpa_auth, group); ++ ++ return ret; ++} ++ ++ + int wpa_auth_sta_set_vlan(struct wpa_state_machine *sm, int vlan_id) + { + struct wpa_group *group; +@@ -3478,6 +4777,14 @@ + (timeout_ms % 1000) * 1000, + wpa_send_eapol_timeout, wpa_auth, sm); + } ++ ++#ifdef CONFIG_TESTING_OPTIONS ++ if (sm->eapol_status_cb) { ++ sm->eapol_status_cb(sm->eapol_status_cb_ctx1, ++ sm->eapol_status_cb_ctx2); ++ sm->eapol_status_cb = NULL; ++ } ++#endif /* CONFIG_TESTING_OPTIONS */ + } + + +@@ -3524,3 +4831,379 @@ + for (group = wpa_auth->group; group; group = group->next) + wpa_group_config_group_keys(wpa_auth, group); + } ++ ++ ++#ifdef CONFIG_FILS ++ ++struct wpa_auth_fils_iter_data { ++ struct wpa_authenticator *auth; ++ const u8 *cache_id; ++ struct rsn_pmksa_cache_entry *pmksa; ++ const u8 *spa; ++ const u8 *pmkid; ++}; ++ ++ ++static int wpa_auth_fils_iter(struct wpa_authenticator *a, void *ctx) ++{ ++ struct wpa_auth_fils_iter_data *data = ctx; ++ ++ if (a == data->auth || !a->conf.fils_cache_id_set || ++ os_memcmp(a->conf.fils_cache_id, data->cache_id, ++ FILS_CACHE_ID_LEN) != 0) ++ return 0; ++ data->pmksa = pmksa_cache_auth_get(a->pmksa, data->spa, data->pmkid); ++ return data->pmksa != NULL; ++} ++ ++ ++struct rsn_pmksa_cache_entry * ++wpa_auth_pmksa_get_fils_cache_id(struct wpa_authenticator *wpa_auth, ++ const u8 *sta_addr, const u8 *pmkid) ++{ ++ struct wpa_auth_fils_iter_data idata; ++ ++ if (!wpa_auth->conf.fils_cache_id_set) ++ return NULL; ++ idata.auth = wpa_auth; ++ idata.cache_id = wpa_auth->conf.fils_cache_id; ++ idata.pmksa = NULL; ++ idata.spa = sta_addr; ++ idata.pmkid = pmkid; ++ wpa_auth_for_each_auth(wpa_auth, wpa_auth_fils_iter, &idata); ++ return idata.pmksa; ++} ++ ++ ++#ifdef CONFIG_IEEE80211R_AP ++int wpa_auth_write_fte(struct wpa_authenticator *wpa_auth, int use_sha384, ++ u8 *buf, size_t len) ++{ ++ struct wpa_auth_config *conf = &wpa_auth->conf; ++ ++ return wpa_write_ftie(conf, use_sha384, conf->r0_key_holder, ++ conf->r0_key_holder_len, ++ NULL, NULL, buf, len, NULL, 0); ++} ++#endif /* CONFIG_IEEE80211R_AP */ ++ ++ ++void wpa_auth_get_fils_aead_params(struct wpa_state_machine *sm, ++ u8 *fils_anonce, u8 *fils_snonce, ++ u8 *fils_kek, size_t *fils_kek_len) ++{ ++ os_memcpy(fils_anonce, sm->ANonce, WPA_NONCE_LEN); ++ os_memcpy(fils_snonce, sm->SNonce, WPA_NONCE_LEN); ++ os_memcpy(fils_kek, sm->PTK.kek, WPA_KEK_MAX_LEN); ++ *fils_kek_len = sm->PTK.kek_len; ++} ++ ++ ++void wpa_auth_add_fils_pmk_pmkid(struct wpa_state_machine *sm, const u8 *pmk, ++ size_t pmk_len, const u8 *pmkid) ++{ ++ os_memcpy(sm->PMK, pmk, pmk_len); ++ sm->pmk_len = pmk_len; ++ os_memcpy(sm->pmkid, pmkid, PMKID_LEN); ++ sm->pmkid_set = 1; ++} ++ ++#endif /* CONFIG_FILS */ ++ ++ ++void wpa_auth_set_auth_alg(struct wpa_state_machine *sm, u16 auth_alg) ++{ ++ if (sm) ++ sm->auth_alg = auth_alg; ++} ++ ++ ++#ifdef CONFIG_DPP2 ++void wpa_auth_set_dpp_z(struct wpa_state_machine *sm, const struct wpabuf *z) ++{ ++ if (sm) { ++ wpabuf_clear_free(sm->dpp_z); ++ sm->dpp_z = z ? wpabuf_dup(z) : NULL; ++ } ++} ++#endif /* CONFIG_DPP2 */ ++ ++ ++#ifdef CONFIG_TESTING_OPTIONS ++ ++int wpa_auth_resend_m1(struct wpa_state_machine *sm, int change_anonce, ++ void (*cb)(void *ctx1, void *ctx2), ++ void *ctx1, void *ctx2) ++{ ++ const u8 *anonce = sm->ANonce; ++ u8 anonce_buf[WPA_NONCE_LEN]; ++ ++ if (change_anonce) { ++ if (random_get_bytes(anonce_buf, WPA_NONCE_LEN)) ++ return -1; ++ anonce = anonce_buf; ++ } ++ ++ wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG, ++ "sending 1/4 msg of 4-Way Handshake (TESTING)"); ++ wpa_send_eapol(sm->wpa_auth, sm, ++ WPA_KEY_INFO_ACK | WPA_KEY_INFO_KEY_TYPE, NULL, ++ anonce, NULL, 0, 0, 0); ++ return 0; ++} ++ ++ ++int wpa_auth_resend_m3(struct wpa_state_machine *sm, ++ void (*cb)(void *ctx1, void *ctx2), ++ void *ctx1, void *ctx2) ++{ ++ u8 rsc[WPA_KEY_RSC_LEN], *_rsc, *gtk, *kde, *pos; ++#ifdef CONFIG_IEEE80211W ++ u8 *opos; ++#endif /* CONFIG_IEEE80211W */ ++ size_t gtk_len, kde_len; ++ struct wpa_group *gsm = sm->group; ++ u8 *wpa_ie; ++ int wpa_ie_len, secure, keyidx, encr = 0; ++ ++ /* Send EAPOL(1, 1, 1, Pair, P, RSC, ANonce, MIC(PTK), RSNIE, [MDIE], ++ GTK[GN], IGTK, [FTIE], [TIE * 2]) ++ */ ++ ++ /* Use 0 RSC */ ++ os_memset(rsc, 0, WPA_KEY_RSC_LEN); ++ /* If FT is used, wpa_auth->wpa_ie includes both RSNIE and MDIE */ ++ wpa_ie = sm->wpa_auth->wpa_ie; ++ wpa_ie_len = sm->wpa_auth->wpa_ie_len; ++ if (sm->wpa == WPA_VERSION_WPA && ++ (sm->wpa_auth->conf.wpa & WPA_PROTO_RSN) && ++ wpa_ie_len > wpa_ie[1] + 2 && wpa_ie[0] == WLAN_EID_RSN) { ++ /* WPA-only STA, remove RSN IE and possible MDIE */ ++ wpa_ie = wpa_ie + wpa_ie[1] + 2; ++ if (wpa_ie[0] == WLAN_EID_MOBILITY_DOMAIN) ++ wpa_ie = wpa_ie + wpa_ie[1] + 2; ++ wpa_ie_len = wpa_ie[1] + 2; ++ } ++ wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG, ++ "sending 3/4 msg of 4-Way Handshake (TESTING)"); ++ 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; ++ 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) + ocv_oci_len(sm); ++ if (gtk) ++ kde_len += 2 + RSN_SELECTOR_LEN + 2 + gtk_len; ++#ifdef CONFIG_IEEE80211R_AP ++ 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_AP */ ++ kde = os_malloc(kde_len); ++ if (kde == NULL) ++ return -1; ++ ++ pos = kde; ++ os_memcpy(pos, wpa_ie, wpa_ie_len); ++ pos += wpa_ie_len; ++#ifdef CONFIG_IEEE80211R_AP ++ if (wpa_key_mgmt_ft(sm->wpa_key_mgmt)) { ++ int res; ++ size_t elen; ++ ++ elen = pos - kde; ++ res = wpa_insert_pmkid(kde, &elen, 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 -1; ++ } ++ pos -= wpa_ie_len; ++ pos += elen; ++ } ++#endif /* CONFIG_IEEE80211R_AP */ ++ 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); ++ } ++#ifdef CONFIG_IEEE80211W ++ opos = pos; ++ pos = ieee80211w_kde_add(sm, pos); ++ if (pos - opos >= 2 + RSN_SELECTOR_LEN + WPA_IGTK_KDE_PREFIX_LEN) { ++ /* skip KDE header and keyid */ ++ opos += 2 + RSN_SELECTOR_LEN + 2; ++ os_memset(opos, 0, 6); /* clear PN */ ++ } ++#endif /* CONFIG_IEEE80211W */ ++ if (ocv_oci_add(sm, &pos) < 0) { ++ os_free(kde); ++ return -1; ++ } ++ ++#ifdef CONFIG_IEEE80211R_AP ++ if (wpa_key_mgmt_ft(sm->wpa_key_mgmt)) { ++ int res; ++ struct wpa_auth_config *conf; ++ ++ conf = &sm->wpa_auth->conf; ++ if (sm->assoc_resp_ftie && ++ kde + kde_len - pos >= 2 + sm->assoc_resp_ftie[1]) { ++ os_memcpy(pos, sm->assoc_resp_ftie, ++ 2 + sm->assoc_resp_ftie[1]); ++ res = 2 + sm->assoc_resp_ftie[1]; ++ } else { ++ int use_sha384 = wpa_key_mgmt_sha384(sm->wpa_key_mgmt); ++ ++ res = wpa_write_ftie(conf, use_sha384, ++ 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 -1; ++ } ++ 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); ++ pos += 4; ++ } ++#endif /* CONFIG_IEEE80211R_AP */ ++ ++ wpa_send_eapol(sm->wpa_auth, sm, ++ (secure ? WPA_KEY_INFO_SECURE : 0) | ++ (wpa_mic_len(sm->wpa_key_mgmt, sm->pmk_len) ? ++ WPA_KEY_INFO_MIC : 0) | ++ WPA_KEY_INFO_ACK | WPA_KEY_INFO_INSTALL | ++ WPA_KEY_INFO_KEY_TYPE, ++ _rsc, sm->ANonce, kde, pos - kde, keyidx, encr); ++ os_free(kde); ++ return 0; ++} ++ ++ ++int wpa_auth_resend_group_m1(struct wpa_state_machine *sm, ++ void (*cb)(void *ctx1, void *ctx2), ++ void *ctx1, void *ctx2) ++{ ++ u8 rsc[WPA_KEY_RSC_LEN]; ++ struct wpa_group *gsm = sm->group; ++ const u8 *kde; ++ u8 *kde_buf = NULL, *pos, hdr[2]; ++#ifdef CONFIG_IEEE80211W ++ u8 *opos; ++#endif /* CONFIG_IEEE80211W */ ++ size_t kde_len; ++ u8 *gtk; ++ ++ /* Send EAPOL(1, 1, 1, !Pair, G, RSC, GNonce, MIC(PTK), GTK[GN]) */ ++ os_memset(rsc, 0, WPA_KEY_RSC_LEN); ++ /* Use 0 RSC */ ++ wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG, ++ "sending 1/2 msg of Group Key Handshake (TESTING)"); ++ ++ gtk = gsm->GTK[gsm->GN - 1]; ++ if (sm->wpa == WPA_VERSION_WPA2) { ++ kde_len = 2 + RSN_SELECTOR_LEN + 2 + gsm->GTK_len + ++ ieee80211w_kde_len(sm) + ocv_oci_len(sm); ++ kde_buf = os_malloc(kde_len); ++ if (kde_buf == NULL) ++ return -1; ++ ++ kde = pos = kde_buf; ++ hdr[0] = gsm->GN & 0x03; ++ hdr[1] = 0; ++ pos = wpa_add_kde(pos, RSN_KEY_DATA_GROUPKEY, hdr, 2, ++ gtk, gsm->GTK_len); ++#ifdef CONFIG_IEEE80211W ++ opos = pos; ++ pos = ieee80211w_kde_add(sm, pos); ++ if (pos - opos >= ++ 2 + RSN_SELECTOR_LEN + WPA_IGTK_KDE_PREFIX_LEN) { ++ /* skip KDE header and keyid */ ++ opos += 2 + RSN_SELECTOR_LEN + 2; ++ os_memset(opos, 0, 6); /* clear PN */ ++ } ++#endif /* CONFIG_IEEE80211W */ ++ if (ocv_oci_add(sm, &pos) < 0) { ++ os_free(kde_buf); ++ return -1; ++ } ++ kde_len = pos - kde; ++ } else { ++ kde = gtk; ++ kde_len = gsm->GTK_len; ++ } ++ ++ sm->eapol_status_cb = cb; ++ sm->eapol_status_cb_ctx1 = ctx1; ++ sm->eapol_status_cb_ctx2 = ctx2; ++ ++ wpa_send_eapol(sm->wpa_auth, sm, ++ WPA_KEY_INFO_SECURE | ++ (wpa_mic_len(sm->wpa_key_mgmt, sm->pmk_len) ? ++ WPA_KEY_INFO_MIC : 0) | ++ WPA_KEY_INFO_ACK | ++ (!sm->Pair ? WPA_KEY_INFO_INSTALL : 0), ++ rsc, NULL, kde, kde_len, gsm->GN, 1); ++ ++ os_free(kde_buf); ++ return 0; ++} ++ ++ ++int wpa_auth_rekey_gtk(struct wpa_authenticator *wpa_auth) ++{ ++ if (!wpa_auth) ++ return -1; ++ eloop_cancel_timeout(wpa_rekey_gtk, wpa_auth, NULL); ++ return eloop_register_timeout(0, 0, wpa_rekey_gtk, wpa_auth, NULL); ++} ++ ++#endif /* CONFIG_TESTING_OPTIONS */ +--- contrib/wpa/src/ap/wpa_auth.h.orig ++++ contrib/wpa/src/ap/wpa_auth.h +@@ -1,6 +1,6 @@ + /* + * hostapd - IEEE 802.11i-2004 / WPA Authenticator +- * Copyright (c) 2004-2015, Jouni Malinen ++ * Copyright (c) 2004-2017, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. +@@ -14,6 +14,8 @@ + #include "common/wpa_common.h" + #include "common/ieee802_11_defs.h" + ++struct vlan_description; ++ + #define MAX_OWN_IE_OVERRIDE 256 + + #ifdef _MSC_VER +@@ -37,65 +39,100 @@ + + #define FT_PACKET_REQUEST 0 + #define FT_PACKET_RESPONSE 1 +-/* Vendor-specific types for R0KH-R1KH protocol; not defined in 802.11r */ +-#define FT_PACKET_R0KH_R1KH_PULL 200 +-#define FT_PACKET_R0KH_R1KH_RESP 201 +-#define FT_PACKET_R0KH_R1KH_PUSH 202 + +-#define FT_R0KH_R1KH_PULL_DATA_LEN 44 +-#define FT_R0KH_R1KH_RESP_DATA_LEN 76 +-#define FT_R0KH_R1KH_PUSH_DATA_LEN 88 +-#define FT_R0KH_R1KH_PULL_NONCE_LEN 16 ++/* Vendor-specific types for R0KH-R1KH protocol; not defined in 802.11r. These ++ * use OUI Extended EtherType as the encapsulating format. */ ++#define FT_PACKET_R0KH_R1KH_PULL 0x01 ++#define FT_PACKET_R0KH_R1KH_RESP 0x02 ++#define FT_PACKET_R0KH_R1KH_PUSH 0x03 ++#define FT_PACKET_R0KH_R1KH_SEQ_REQ 0x04 ++#define FT_PACKET_R0KH_R1KH_SEQ_RESP 0x05 + +-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]; ++/* packet layout ++ * IEEE 802 extended OUI ethertype frame header ++ * u16 authlen (little endian) ++ * multiple of struct ft_rrb_tlv (authenticated only, length = authlen) ++ * multiple of struct ft_rrb_tlv (AES-SIV encrypted, AES-SIV needs an extra ++ * blocksize length) ++ * ++ * AES-SIV AAD; ++ * source MAC address (6) ++ * authenticated-only TLVs (authlen) ++ * subtype (1; FT_PACKET_*) ++ */ + +- u8 nonce[FT_R0KH_R1KH_PULL_NONCE_LEN]; +- u8 pmk_r0_name[WPA_PMK_NAME_LEN]; +- u8 r1kh_id[FT_R1KH_ID_LEN]; +- u8 s1kh_id[ETH_ALEN]; +- u8 pad[4]; /* 8-octet boundary for AES key wrap */ +- u8 key_wrap_extra[8]; +-} STRUCT_PACKED; ++#define FT_RRB_NONCE_LEN 16 + +-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]; ++#define FT_RRB_LAST_EMPTY 0 /* placeholder or padding */ + +- u8 nonce[FT_R0KH_R1KH_PULL_NONCE_LEN]; /* copied from pull */ +- u8 r1kh_id[FT_R1KH_ID_LEN]; /* copied from pull */ +- u8 s1kh_id[ETH_ALEN]; /* copied from pull */ +- u8 pmk_r1[PMK_LEN]; +- u8 pmk_r1_name[WPA_PMK_NAME_LEN]; +- le16 pairwise; +- u8 pad[2]; /* 8-octet boundary for AES key wrap */ +- u8 key_wrap_extra[8]; +-} STRUCT_PACKED; ++#define FT_RRB_SEQ 1 /* struct ft_rrb_seq */ ++#define FT_RRB_NONCE 2 /* size FT_RRB_NONCE_LEN */ ++#define FT_RRB_TIMESTAMP 3 /* le32 unix seconds */ + +-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]; ++#define FT_RRB_R0KH_ID 4 /* FT_R0KH_ID_MAX_LEN */ ++#define FT_RRB_R1KH_ID 5 /* FT_R1KH_ID_LEN */ ++#define FT_RRB_S1KH_ID 6 /* 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]; ++#define FT_RRB_PMK_R0_NAME 7 /* WPA_PMK_NAME_LEN */ ++#define FT_RRB_PMK_R0 8 /* PMK_LEN */ ++#define FT_RRB_PMK_R1_NAME 9 /* WPA_PMK_NAME_LEN */ ++#define FT_RRB_PMK_R1 10 /* PMK_LEN */ ++ ++#define FT_RRB_PAIRWISE 11 /* le16 */ ++#define FT_RRB_EXPIRES_IN 12 /* le16 seconds */ ++ ++#define FT_RRB_VLAN_UNTAGGED 13 /* le16 */ ++#define FT_RRB_VLAN_TAGGED 14 /* n times le16 */ ++ ++#define FT_RRB_IDENTITY 15 ++#define FT_RRB_RADIUS_CUI 16 ++#define FT_RRB_SESSION_TIMEOUT 17 /* le32 seconds */ ++ ++struct ft_rrb_tlv { ++ le16 type; ++ le16 len; ++ /* followed by data of length len */ + } STRUCT_PACKED; + ++struct ft_rrb_seq { ++ le32 dom; ++ le32 seq; ++ le32 ts; ++} STRUCT_PACKED; ++ ++/* session TLVs: ++ * required: PMK_R1, PMK_R1_NAME, PAIRWISE ++ * optional: VLAN_UNTAGGED, VLAN_TAGGED, EXPIRES_IN, IDENTITY, RADIUS_CUI, ++ * SESSION_TIMEOUT ++ * ++ * pull frame TLVs: ++ * auth: ++ * required: SEQ, NONCE, R0KH_ID, R1KH_ID ++ * encrypted: ++ * required: PMK_R0_NAME, S1KH_ID ++ * ++ * response frame TLVs: ++ * auth: ++ * required: SEQ, NONCE, R0KH_ID, R1KH_ID ++ * encrypted: ++ * required: S1KH_ID ++ * optional: session TLVs ++ * ++ * push frame TLVs: ++ * auth: ++ * required: SEQ, R0KH_ID, R1KH_ID ++ * encrypted: ++ * required: S1KH_ID, PMK_R0_NAME, session TLVs ++ * ++ * sequence number request frame TLVs: ++ * auth: ++ * required: R0KH_ID, R1KH_ID, NONCE ++ * ++ * sequence number response frame TLVs: ++ * auth: ++ * required: SEQ, NONCE, R0KH_ID, R1KH_ID ++ */ ++ + #ifdef _MSC_VER + #pragma pack(pop) + #endif /* _MSC_VER */ +@@ -107,6 +144,8 @@ + struct wpa_state_machine; + struct rsn_pmksa_cache_entry; + struct eapol_state_machine; ++struct ft_remote_seq; ++struct wpa_channel_info; + + + struct ft_remote_r0kh { +@@ -114,7 +153,8 @@ + u8 addr[ETH_ALEN]; + u8 id[FT_R0KH_ID_MAX_LEN]; + size_t id_len; +- u8 key[16]; ++ u8 key[32]; ++ struct ft_remote_seq *seq; + }; + + +@@ -122,7 +162,8 @@ + struct ft_remote_r1kh *next; + u8 addr[ETH_ALEN]; + u8 id[FT_R1KH_ID_LEN]; +- u8 key[16]; ++ u8 key[32]; ++ struct ft_remote_seq *seq; + }; + + +@@ -135,10 +176,12 @@ + int wpa_strict_rekey; + int wpa_gmk_rekey; + int wpa_ptk_rekey; ++ u32 wpa_group_update_count; ++ u32 wpa_pairwise_update_count; ++ int wpa_disable_eapol_key_retries; + int rsn_pairwise; + int rsn_preauth; + int eapol_version; +- int peerkey; + int wmm_enabled; + int wmm_uapsd; + int disable_pmksa_caching; +@@ -147,8 +190,12 @@ + #ifdef CONFIG_IEEE80211W + enum mfp_options ieee80211w; + int group_mgmt_cipher; ++ int sae_require_mfp; + #endif /* CONFIG_IEEE80211W */ +-#ifdef CONFIG_IEEE80211R ++#ifdef CONFIG_OCV ++ int ocv; /* Operating Channel Validation */ ++#endif /* CONFIG_OCV */ ++#ifdef CONFIG_IEEE80211R_AP + u8 ssid[SSID_MAX_LEN]; + size_t ssid_len; + u8 mobility_domain[MOBILITY_DOMAIN_ID_LEN]; +@@ -155,13 +202,19 @@ + 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 r0_key_lifetime; /* PMK-R0 lifetime seconds */ ++ int rkh_pos_timeout; ++ int rkh_neg_timeout; ++ int rkh_pull_timeout; /* ms */ ++ int rkh_pull_retries; ++ int r1_max_key_lifetime; + u32 reassociation_deadline; +- struct ft_remote_r0kh *r0kh_list; +- struct ft_remote_r1kh *r1kh_list; ++ struct ft_remote_r0kh **r0kh_list; ++ struct ft_remote_r1kh **r1kh_list; + int pmk_r1_push; + int ft_over_ds; +-#endif /* CONFIG_IEEE80211R */ ++ int ft_psk_generate_local; ++#endif /* CONFIG_IEEE80211R_AP */ + int disable_gtk; + int ap_mlme; + #ifdef CONFIG_TESTING_OPTIONS +@@ -175,6 +228,10 @@ + u8 ip_addr_start[4]; + u8 ip_addr_end[4]; + #endif /* CONFIG_P2P */ ++#ifdef CONFIG_FILS ++ unsigned int fils_cache_id_set:1; ++ u8 fils_cache_id[FILS_CACHE_ID_LEN]; ++#endif /* CONFIG_FILS */ + }; + + typedef enum { +@@ -188,7 +245,6 @@ + } 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); +@@ -198,7 +254,8 @@ + int value); + int (*get_eapol)(void *ctx, const u8 *addr, wpa_eapol_variable var); + const u8 * (*get_psk)(void *ctx, const u8 *addr, const u8 *p2p_dev_addr, +- const u8 *prev_psk); ++ const u8 *prev_psk, size_t *psk_len, ++ int *vlan_id); + 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); +@@ -211,13 +268,34 @@ + 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 ++ int (*send_oui)(void *ctx, const u8 *dst, u8 oui_suffix, const u8 *data, ++ size_t data_len); ++ int (*channel_info)(void *ctx, struct wpa_channel_info *ci); ++ int (*update_vlan)(void *ctx, const u8 *addr, int vlan_id); ++ int (*get_sta_tx_params)(void *ctx, const u8 *addr, ++ int ap_max_chanwidth, int ap_seg1_idx, ++ int *bandwidth, int *seg1_idx); ++#ifdef CONFIG_IEEE80211R_AP + struct wpa_state_machine * (*add_sta)(void *ctx, const u8 *sta_addr); ++ int (*set_vlan)(void *ctx, const u8 *sta_addr, ++ struct vlan_description *vlan); ++ int (*get_vlan)(void *ctx, const u8 *sta_addr, ++ struct vlan_description *vlan); ++ int (*set_identity)(void *ctx, const u8 *sta_addr, ++ const u8 *identity, size_t identity_len); ++ size_t (*get_identity)(void *ctx, const u8 *sta_addr, const u8 **buf); ++ int (*set_radius_cui)(void *ctx, const u8 *sta_addr, ++ const u8 *radius_cui, size_t radius_cui_len); ++ size_t (*get_radius_cui)(void *ctx, const u8 *sta_addr, const u8 **buf); ++ void (*set_session_timeout)(void *ctx, const u8 *sta_addr, ++ int session_timeout); ++ int (*get_session_timeout)(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 */ ++#endif /* CONFIG_IEEE80211R_AP */ + #ifdef CONFIG_MESH + int (*start_ampe)(void *ctx, const u8 *sta_addr); + #endif /* CONFIG_MESH */ +@@ -225,7 +303,8 @@ + + struct wpa_authenticator * wpa_init(const u8 *addr, + struct wpa_auth_config *conf, +- struct wpa_auth_callbacks *cb); ++ const struct wpa_auth_callbacks *cb, ++ void *cb_ctx); + 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, +@@ -235,17 +314,20 @@ + 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 ++ WPA_INVALID_MDIE, WPA_INVALID_PROTO, WPA_INVALID_PMKID + }; +- ++ + int wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth, +- struct wpa_state_machine *sm, ++ struct wpa_state_machine *sm, int freq, + const u8 *wpa_ie, size_t wpa_ie_len, +- const u8 *mdie, size_t mdie_len); ++ const u8 *mdie, size_t mdie_len, ++ const u8 *owe_dh, size_t owe_dh_len); + int wpa_validate_osen(struct wpa_authenticator *wpa_auth, + struct wpa_state_machine *sm, + const u8 *osen_ie, size_t osen_ie_len); + int wpa_auth_uses_mfp(struct wpa_state_machine *sm); ++void wpa_auth_set_ocv(struct wpa_state_machine *sm, int ocv); ++int wpa_auth_uses_ocv(struct wpa_state_machine *sm); + struct wpa_state_machine * + wpa_auth_sta_init(struct wpa_authenticator *wpa_auth, const u8 *addr, + const u8 *p2p_dev_addr); +@@ -258,7 +340,7 @@ + u8 *data, size_t data_len); + enum wpa_event { + WPA_AUTH, WPA_ASSOC, WPA_DISASSOC, WPA_DEAUTH, WPA_REAUTH, +- WPA_REAUTH_EAPOL, WPA_ASSOC_FT ++ WPA_REAUTH_EAPOL, WPA_ASSOC_FT, WPA_ASSOC_FILS, WPA_DRV_STA_REMOVED + }; + void wpa_remove_ptk(struct wpa_state_machine *sm); + int wpa_auth_sm_event(struct wpa_state_machine *sm, enum wpa_event event); +@@ -269,9 +351,11 @@ + 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); ++const u8 * wpa_auth_get_pmk(struct wpa_state_machine *sm, int *len); + 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_fils_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 * +@@ -280,6 +364,7 @@ + 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, ++ unsigned int pmk_len, + 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, +@@ -286,14 +371,38 @@ + int session_timeout, + struct eapol_state_machine *eapol); + int wpa_auth_pmksa_add_sae(struct wpa_authenticator *wpa_auth, const u8 *addr, +- const u8 *pmk); ++ const u8 *pmk, const u8 *pmkid); ++void wpa_auth_add_sae_pmkid(struct wpa_state_machine *sm, const u8 *pmkid); ++int wpa_auth_pmksa_add2(struct wpa_authenticator *wpa_auth, const u8 *addr, ++ const u8 *pmk, size_t pmk_len, const u8 *pmkid, ++ int session_timeout, int akmp); + void wpa_auth_pmksa_remove(struct wpa_authenticator *wpa_auth, + const u8 *sta_addr); ++int wpa_auth_pmksa_list(struct wpa_authenticator *wpa_auth, char *buf, ++ size_t len); ++void wpa_auth_pmksa_flush(struct wpa_authenticator *wpa_auth); ++int wpa_auth_pmksa_list_mesh(struct wpa_authenticator *wpa_auth, const u8 *addr, ++ char *buf, size_t len); ++struct rsn_pmksa_cache_entry * ++wpa_auth_pmksa_create_entry(const u8 *aa, const u8 *spa, const u8 *pmk, ++ const u8 *pmkid, int expiration); ++int wpa_auth_pmksa_add_entry(struct wpa_authenticator *wpa_auth, ++ struct rsn_pmksa_cache_entry *entry); ++struct rsn_pmksa_cache_entry * ++wpa_auth_pmksa_get(struct wpa_authenticator *wpa_auth, const u8 *sta_addr, ++ const u8 *pmkid); ++struct rsn_pmksa_cache_entry * ++wpa_auth_pmksa_get_fils_cache_id(struct wpa_authenticator *wpa_auth, ++ const u8 *sta_addr, const u8 *pmkid); ++void wpa_auth_pmksa_set_to_sm(struct rsn_pmksa_cache_entry *pmksa, ++ struct wpa_state_machine *sm, ++ struct wpa_authenticator *wpa_auth, ++ u8 *pmkid, u8 *pmk); + 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 ++#ifdef CONFIG_IEEE80211R_AP + 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); +@@ -308,8 +417,13 @@ + 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_rrb_oui_rx(struct wpa_authenticator *wpa_auth, const u8 *src_addr, ++ const u8 *dst_addr, u8 oui_suffix, 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_ft_deinit(struct wpa_authenticator *wpa_auth); ++void wpa_ft_sta_deinit(struct wpa_state_machine *sm); ++#endif /* CONFIG_IEEE80211R_AP */ + + void wpa_wnmsleep_rekey_gtk(struct wpa_state_machine *sm); + void wpa_set_wnmsleep(struct wpa_state_machine *sm, int flag); +@@ -326,4 +440,54 @@ + struct radius_das_attrs *attr); + void wpa_auth_reconfig_group_keys(struct wpa_authenticator *wpa_auth); + ++int wpa_auth_ensure_group(struct wpa_authenticator *wpa_auth, int vlan_id); ++int wpa_auth_release_group(struct wpa_authenticator *wpa_auth, int vlan_id); ++int fils_auth_pmk_to_ptk(struct wpa_state_machine *sm, const u8 *pmk, ++ size_t pmk_len, const u8 *snonce, const u8 *anonce, ++ const u8 *dhss, size_t dhss_len, ++ struct wpabuf *g_sta, struct wpabuf *g_ap); ++int fils_decrypt_assoc(struct wpa_state_machine *sm, const u8 *fils_session, ++ const struct ieee80211_mgmt *mgmt, size_t frame_len, ++ u8 *pos, size_t left); ++int fils_encrypt_assoc(struct wpa_state_machine *sm, u8 *buf, ++ size_t current_len, size_t max_len, ++ const struct wpabuf *hlp); ++int fils_set_tk(struct wpa_state_machine *sm); ++u8 * hostapd_eid_assoc_fils_session(struct wpa_state_machine *sm, u8 *eid, ++ const u8 *fils_session, ++ struct wpabuf *fils_hlp_resp); ++const u8 * wpa_fils_validate_fils_session(struct wpa_state_machine *sm, ++ const u8 *ies, size_t ies_len, ++ const u8 *fils_session); ++int wpa_fils_validate_key_confirm(struct wpa_state_machine *sm, const u8 *ies, ++ size_t ies_len); ++ ++int get_sta_tx_parameters(struct wpa_state_machine *sm, int ap_max_chanwidth, ++ int ap_seg1_idx, int *bandwidth, int *seg1_idx); ++ ++int wpa_auth_write_fte(struct wpa_authenticator *wpa_auth, int use_sha384, ++ u8 *buf, size_t len); ++void wpa_auth_get_fils_aead_params(struct wpa_state_machine *sm, ++ u8 *fils_anonce, u8 *fils_snonce, ++ u8 *fils_kek, size_t *fils_kek_len); ++void wpa_auth_add_fils_pmk_pmkid(struct wpa_state_machine *sm, const u8 *pmk, ++ size_t pmk_len, const u8 *pmkid); ++u8 * wpa_auth_write_assoc_resp_owe(struct wpa_state_machine *sm, ++ u8 *pos, size_t max_len, ++ const u8 *req_ies, size_t req_ies_len); ++void wpa_auth_set_auth_alg(struct wpa_state_machine *sm, u16 auth_alg); ++void wpa_auth_set_dpp_z(struct wpa_state_machine *sm, const struct wpabuf *z); ++ ++int wpa_auth_resend_m1(struct wpa_state_machine *sm, int change_anonce, ++ void (*cb)(void *ctx1, void *ctx2), ++ void *ctx1, void *ctx2); ++int wpa_auth_resend_m3(struct wpa_state_machine *sm, ++ void (*cb)(void *ctx1, void *ctx2), ++ void *ctx1, void *ctx2); ++int wpa_auth_resend_group_m1(struct wpa_state_machine *sm, ++ void (*cb)(void *ctx1, void *ctx2), ++ void *ctx1, void *ctx2); ++int wpa_auth_rekey_gtk(struct wpa_authenticator *wpa_auth); ++void wpa_auth_set_ptk_rekey_timer(struct wpa_state_machine *sm); ++ + #endif /* WPA_AUTH_H */ +--- contrib/wpa/src/ap/wpa_auth_ft.c.orig ++++ contrib/wpa/src/ap/wpa_auth_ft.c +@@ -1,6 +1,6 @@ + /* + * hostapd - IEEE 802.11r - Fast BSS Transition +- * Copyright (c) 2004-2015, Jouni Malinen ++ * Copyright (c) 2004-2018, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. +@@ -13,7 +13,12 @@ + #include "utils/list.h" + #include "common/ieee802_11_defs.h" + #include "common/ieee802_11_common.h" ++#include "common/ocv.h" ++#include "drivers/driver.h" ++#include "crypto/aes.h" ++#include "crypto/aes_siv.h" + #include "crypto/aes_wrap.h" ++#include "crypto/sha384.h" + #include "crypto/random.h" + #include "ap_config.h" + #include "ieee802_11.h" +@@ -22,57 +27,762 @@ + #include "wpa_auth_i.h" + + +-#ifdef CONFIG_IEEE80211R ++#ifdef CONFIG_IEEE80211R_AP + ++const unsigned int ftRRBseqTimeout = 10; ++const unsigned int ftRRBmaxQueueLen = 100; ++ ++ + static int wpa_ft_send_rrb_auth_resp(struct wpa_state_machine *sm, + const u8 *current_ap, const u8 *sta_addr, + u16 status, const u8 *resp_ies, + size_t resp_ies_len); ++static void ft_finish_pull(struct wpa_state_machine *sm); ++static void wpa_ft_expire_pull(void *eloop_ctx, void *timeout_ctx); ++static void wpa_ft_rrb_seq_timeout(void *eloop_ctx, void *timeout_ctx); + ++struct tlv_list { ++ u16 type; ++ size_t len; ++ const u8 *data; ++}; + ++ ++/** ++ * wpa_ft_rrb_decrypt - Decrypt FT RRB message ++ * @key: AES-SIV key for AEAD ++ * @key_len: Length of key in octets ++ * @enc: Pointer to encrypted TLVs ++ * @enc_len: Length of encrypted TLVs in octets ++ * @auth: Pointer to authenticated TLVs ++ * @auth_len: Length of authenticated TLVs in octets ++ * @src_addr: MAC address of the frame sender ++ * @type: Vendor-specific subtype of the RRB frame (FT_PACKET_*) ++ * @plain: Pointer to return the pointer to the allocated plaintext buffer; ++ * needs to be freed by the caller if not NULL; ++ * will only be returned on success ++ * @plain_len: Pointer to return the length of the allocated plaintext buffer ++ * in octets ++ * Returns: 0 on success, -1 on error ++ */ ++static int wpa_ft_rrb_decrypt(const u8 *key, const size_t key_len, ++ const u8 *enc, size_t enc_len, ++ const u8 *auth, const size_t auth_len, ++ const u8 *src_addr, u8 type, ++ u8 **plain, size_t *plain_size) ++{ ++ const u8 *ad[3] = { src_addr, auth, &type }; ++ size_t ad_len[3] = { ETH_ALEN, auth_len, sizeof(type) }; ++ ++ wpa_printf(MSG_DEBUG, "FT(RRB): src_addr=" MACSTR " type=%u", ++ MAC2STR(src_addr), type); ++ wpa_hexdump_key(MSG_DEBUG, "FT(RRB): decrypt using key", key, key_len); ++ wpa_hexdump(MSG_DEBUG, "FT(RRB): encrypted TLVs", enc, enc_len); ++ wpa_hexdump(MSG_DEBUG, "FT(RRB): authenticated TLVs", auth, auth_len); ++ ++ if (!key) { /* skip decryption */ ++ *plain = os_memdup(enc, enc_len); ++ if (enc_len > 0 && !*plain) ++ goto err; ++ ++ *plain_size = enc_len; ++ ++ return 0; ++ } ++ ++ *plain = NULL; ++ ++ /* SIV overhead */ ++ if (enc_len < AES_BLOCK_SIZE) ++ goto err; ++ ++ *plain = os_zalloc(enc_len - AES_BLOCK_SIZE); ++ if (!*plain) ++ goto err; ++ ++ if (aes_siv_decrypt(key, key_len, enc, enc_len, 3, ad, ad_len, ++ *plain) < 0) { ++ if (enc_len < AES_BLOCK_SIZE + 2) ++ goto err; ++ ++ /* Try to work around Ethernet devices that add extra ++ * two octet padding even if the frame is longer than ++ * the minimum Ethernet frame. */ ++ enc_len -= 2; ++ if (aes_siv_decrypt(key, key_len, enc, enc_len, 3, ad, ad_len, ++ *plain) < 0) ++ goto err; ++ } ++ ++ *plain_size = enc_len - AES_BLOCK_SIZE; ++ wpa_hexdump_key(MSG_DEBUG, "FT(RRB): decrypted TLVs", ++ *plain, *plain_size); ++ return 0; ++err: ++ os_free(*plain); ++ *plain = NULL; ++ *plain_size = 0; ++ ++ wpa_printf(MSG_ERROR, "FT(RRB): Failed to decrypt"); ++ ++ return -1; ++} ++ ++ ++/* get first tlv record in packet matching type ++ * @data (decrypted) packet ++ * @return 0 on success else -1 ++ */ ++static int wpa_ft_rrb_get_tlv(const u8 *plain, size_t plain_len, ++ u16 type, size_t *tlv_len, const u8 **tlv_data) ++{ ++ const struct ft_rrb_tlv *f; ++ size_t left; ++ le16 type16; ++ size_t len; ++ ++ left = plain_len; ++ type16 = host_to_le16(type); ++ ++ while (left >= sizeof(*f)) { ++ f = (const struct ft_rrb_tlv *) plain; ++ ++ left -= sizeof(*f); ++ plain += sizeof(*f); ++ len = le_to_host16(f->len); ++ ++ if (left < len) { ++ wpa_printf(MSG_DEBUG, "FT: RRB message truncated"); ++ break; ++ } ++ ++ if (f->type == type16) { ++ *tlv_len = len; ++ *tlv_data = plain; ++ return 0; ++ } ++ ++ left -= len; ++ plain += len; ++ } ++ ++ return -1; ++} ++ ++ ++static void wpa_ft_rrb_dump(const u8 *plain, const size_t plain_len) ++{ ++ const struct ft_rrb_tlv *f; ++ size_t left; ++ size_t len; ++ ++ left = plain_len; ++ ++ wpa_printf(MSG_DEBUG, "FT: RRB dump message"); ++ while (left >= sizeof(*f)) { ++ f = (const struct ft_rrb_tlv *) plain; ++ ++ left -= sizeof(*f); ++ plain += sizeof(*f); ++ len = le_to_host16(f->len); ++ ++ wpa_printf(MSG_DEBUG, "FT: RRB TLV type = %d, len = %zu", ++ le_to_host16(f->type), len); ++ ++ if (left < len) { ++ wpa_printf(MSG_DEBUG, ++ "FT: RRB message truncated: left %zu bytes, need %zu", ++ left, len); ++ break; ++ } ++ ++ wpa_hexdump(MSG_DEBUG, "FT: RRB TLV data", plain, len); ++ ++ left -= len; ++ plain += len; ++ } ++ ++ if (left > 0) ++ wpa_hexdump(MSG_DEBUG, "FT: RRB TLV padding", plain, left); ++ ++ wpa_printf(MSG_DEBUG, "FT: RRB dump message end"); ++} ++ ++ ++static int cmp_int(const void *a, const void *b) ++{ ++ int x, y; ++ ++ x = *((int *) a); ++ y = *((int *) b); ++ return x - y; ++} ++ ++ ++static int wpa_ft_rrb_get_tlv_vlan(const u8 *plain, const size_t plain_len, ++ struct vlan_description *vlan) ++{ ++ struct ft_rrb_tlv *f; ++ size_t left; ++ size_t len; ++ int taggedidx; ++ int vlan_id; ++ int type; ++ ++ left = plain_len; ++ taggedidx = 0; ++ os_memset(vlan, 0, sizeof(*vlan)); ++ ++ while (left >= sizeof(*f)) { ++ f = (struct ft_rrb_tlv *) plain; ++ ++ left -= sizeof(*f); ++ plain += sizeof(*f); ++ ++ len = le_to_host16(f->len); ++ type = le_to_host16(f->type); ++ ++ if (left < len) { ++ wpa_printf(MSG_DEBUG, "FT: RRB message truncated"); ++ return -1; ++ } ++ ++ if (type != FT_RRB_VLAN_UNTAGGED && type != FT_RRB_VLAN_TAGGED) ++ goto skip; ++ ++ if (type == FT_RRB_VLAN_UNTAGGED && len != sizeof(le16)) { ++ wpa_printf(MSG_DEBUG, ++ "FT: RRB VLAN_UNTAGGED invalid length"); ++ return -1; ++ } ++ ++ if (type == FT_RRB_VLAN_TAGGED && len % sizeof(le16) != 0) { ++ wpa_printf(MSG_DEBUG, ++ "FT: RRB VLAN_TAGGED invalid length"); ++ return -1; ++ } ++ ++ while (len >= sizeof(le16)) { ++ vlan_id = WPA_GET_LE16(plain); ++ plain += sizeof(le16); ++ left -= sizeof(le16); ++ len -= sizeof(le16); ++ ++ if (vlan_id <= 0 || vlan_id > MAX_VLAN_ID) { ++ wpa_printf(MSG_DEBUG, ++ "FT: RRB VLAN ID invalid %d", ++ vlan_id); ++ continue; ++ } ++ ++ if (type == FT_RRB_VLAN_UNTAGGED) ++ vlan->untagged = vlan_id; ++ ++ if (type == FT_RRB_VLAN_TAGGED && ++ taggedidx < MAX_NUM_TAGGED_VLAN) { ++ vlan->tagged[taggedidx] = vlan_id; ++ taggedidx++; ++ } else if (type == FT_RRB_VLAN_TAGGED) { ++ wpa_printf(MSG_DEBUG, "FT: RRB too many VLANs"); ++ } ++ } ++ ++ skip: ++ left -= len; ++ plain += len; ++ } ++ ++ if (taggedidx) ++ qsort(vlan->tagged, taggedidx, sizeof(int), cmp_int); ++ ++ vlan->notempty = vlan->untagged || vlan->tagged[0]; ++ ++ return 0; ++} ++ ++ ++static size_t wpa_ft_tlv_len(const struct tlv_list *tlvs) ++{ ++ size_t tlv_len = 0; ++ int i; ++ ++ if (!tlvs) ++ return 0; ++ ++ for (i = 0; tlvs[i].type != FT_RRB_LAST_EMPTY; i++) { ++ tlv_len += sizeof(struct ft_rrb_tlv); ++ tlv_len += tlvs[i].len; ++ } ++ ++ return tlv_len; ++} ++ ++ ++static size_t wpa_ft_tlv_lin(const struct tlv_list *tlvs, u8 *start, ++ u8 *endpos) ++{ ++ int i; ++ size_t tlv_len; ++ struct ft_rrb_tlv *hdr; ++ u8 *pos; ++ ++ if (!tlvs) ++ return 0; ++ ++ tlv_len = 0; ++ pos = start; ++ for (i = 0; tlvs[i].type != FT_RRB_LAST_EMPTY; i++) { ++ if (tlv_len + sizeof(*hdr) > (size_t) (endpos - start)) ++ return tlv_len; ++ tlv_len += sizeof(*hdr); ++ hdr = (struct ft_rrb_tlv *) pos; ++ hdr->type = host_to_le16(tlvs[i].type); ++ hdr->len = host_to_le16(tlvs[i].len); ++ pos = start + tlv_len; ++ ++ if (tlv_len + tlvs[i].len > (size_t) (endpos - start)) ++ return tlv_len; ++ if (tlvs[i].len == 0) ++ continue; ++ tlv_len += tlvs[i].len; ++ os_memcpy(pos, tlvs[i].data, tlvs[i].len); ++ pos = start + tlv_len; ++ } ++ ++ return tlv_len; ++} ++ ++ ++static size_t wpa_ft_vlan_len(const struct vlan_description *vlan) ++{ ++ size_t tlv_len = 0; ++ int i; ++ ++ if (!vlan || !vlan->notempty) ++ return 0; ++ ++ if (vlan->untagged) { ++ tlv_len += sizeof(struct ft_rrb_tlv); ++ tlv_len += sizeof(le16); ++ } ++ if (vlan->tagged[0]) ++ tlv_len += sizeof(struct ft_rrb_tlv); ++ for (i = 0; i < MAX_NUM_TAGGED_VLAN && vlan->tagged[i]; i++) ++ tlv_len += sizeof(le16); ++ ++ return tlv_len; ++} ++ ++ ++static size_t wpa_ft_vlan_lin(const struct vlan_description *vlan, ++ u8 *start, u8 *endpos) ++{ ++ size_t tlv_len; ++ int i, len; ++ struct ft_rrb_tlv *hdr; ++ u8 *pos = start; ++ ++ if (!vlan || !vlan->notempty) ++ return 0; ++ ++ tlv_len = 0; ++ if (vlan->untagged) { ++ tlv_len += sizeof(*hdr); ++ if (start + tlv_len > endpos) ++ return tlv_len; ++ hdr = (struct ft_rrb_tlv *) pos; ++ hdr->type = host_to_le16(FT_RRB_VLAN_UNTAGGED); ++ hdr->len = host_to_le16(sizeof(le16)); ++ pos = start + tlv_len; ++ ++ tlv_len += sizeof(le16); ++ if (start + tlv_len > endpos) ++ return tlv_len; ++ WPA_PUT_LE16(pos, vlan->untagged); ++ pos = start + tlv_len; ++ } ++ ++ if (!vlan->tagged[0]) ++ return tlv_len; ++ ++ tlv_len += sizeof(*hdr); ++ if (start + tlv_len > endpos) ++ return tlv_len; ++ hdr = (struct ft_rrb_tlv *) pos; ++ hdr->type = host_to_le16(FT_RRB_VLAN_TAGGED); ++ len = 0; /* len is computed below */ ++ pos = start + tlv_len; ++ ++ for (i = 0; i < MAX_NUM_TAGGED_VLAN && vlan->tagged[i]; i++) { ++ tlv_len += sizeof(le16); ++ if (start + tlv_len > endpos) ++ break; ++ len += sizeof(le16); ++ WPA_PUT_LE16(pos, vlan->tagged[i]); ++ pos = start + tlv_len; ++ } ++ ++ hdr->len = host_to_le16(len); ++ ++ return tlv_len; ++} ++ ++ ++static int wpa_ft_rrb_lin(const struct tlv_list *tlvs1, ++ const struct tlv_list *tlvs2, ++ const struct vlan_description *vlan, ++ u8 **plain, size_t *plain_len) ++{ ++ u8 *pos, *endpos; ++ size_t tlv_len; ++ ++ tlv_len = wpa_ft_tlv_len(tlvs1); ++ tlv_len += wpa_ft_tlv_len(tlvs2); ++ tlv_len += wpa_ft_vlan_len(vlan); ++ ++ *plain_len = tlv_len; ++ *plain = os_zalloc(tlv_len); ++ if (!*plain) { ++ wpa_printf(MSG_ERROR, "FT: Failed to allocate plaintext"); ++ goto err; ++ } ++ ++ pos = *plain; ++ endpos = *plain + tlv_len; ++ pos += wpa_ft_tlv_lin(tlvs1, pos, endpos); ++ pos += wpa_ft_tlv_lin(tlvs2, pos, endpos); ++ pos += wpa_ft_vlan_lin(vlan, pos, endpos); ++ ++ /* sanity check */ ++ if (pos != endpos) { ++ wpa_printf(MSG_ERROR, "FT: Length error building RRB"); ++ goto err; ++ } ++ ++ return 0; ++ ++err: ++ os_free(*plain); ++ *plain = NULL; ++ *plain_len = 0; ++ return -1; ++} ++ ++ ++static int wpa_ft_rrb_encrypt(const u8 *key, const size_t key_len, ++ const u8 *plain, const size_t plain_len, ++ const u8 *auth, const size_t auth_len, ++ const u8 *src_addr, u8 type, u8 *enc) ++{ ++ const u8 *ad[3] = { src_addr, auth, &type }; ++ size_t ad_len[3] = { ETH_ALEN, auth_len, sizeof(type) }; ++ ++ wpa_printf(MSG_DEBUG, "FT(RRB): src_addr=" MACSTR " type=%u", ++ MAC2STR(src_addr), type); ++ wpa_hexdump_key(MSG_DEBUG, "FT(RRB): plaintext message", ++ plain, plain_len); ++ wpa_hexdump_key(MSG_DEBUG, "FT(RRB): encrypt using key", key, key_len); ++ wpa_hexdump(MSG_DEBUG, "FT(RRB): authenticated TLVs", auth, auth_len); ++ ++ if (!key) { ++ /* encryption not needed, return plaintext as packet */ ++ os_memcpy(enc, plain, plain_len); ++ } else if (aes_siv_encrypt(key, key_len, plain, plain_len, ++ 3, ad, ad_len, enc) < 0) { ++ wpa_printf(MSG_ERROR, "FT: Failed to encrypt RRB-OUI message"); ++ return -1; ++ } ++ wpa_hexdump(MSG_DEBUG, "FT(RRB): encrypted TLVs", ++ enc, plain_len + AES_BLOCK_SIZE); ++ ++ return 0; ++} ++ ++ ++/** ++ * wpa_ft_rrb_build - Build and encrypt an FT RRB message ++ * @key: AES-SIV key for AEAD ++ * @key_len: Length of key in octets ++ * @tlvs_enc0: First set of to-be-encrypted TLVs ++ * @tlvs_enc1: Second set of to-be-encrypted TLVs ++ * @tlvs_auth: Set of to-be-authenticated TLVs ++ * @src_addr: MAC address of the frame sender ++ * @type: Vendor-specific subtype of the RRB frame (FT_PACKET_*) ++ * @packet Pointer to return the pointer to the allocated packet buffer; ++ * needs to be freed by the caller if not null; ++ * will only be returned on success ++ * @packet_len: Pointer to return the length of the allocated buffer in octets ++ * Returns: 0 on success, -1 on error ++ */ ++static int wpa_ft_rrb_build(const u8 *key, const size_t key_len, ++ const struct tlv_list *tlvs_enc0, ++ const struct tlv_list *tlvs_enc1, ++ const struct tlv_list *tlvs_auth, ++ const struct vlan_description *vlan, ++ const u8 *src_addr, u8 type, ++ u8 **packet, size_t *packet_len) ++{ ++ u8 *plain = NULL, *auth = NULL, *pos, *tmp; ++ size_t plain_len = 0, auth_len = 0; ++ int ret = -1; ++ size_t pad_len = 0; ++ ++ *packet = NULL; ++ if (wpa_ft_rrb_lin(tlvs_enc0, tlvs_enc1, vlan, &plain, &plain_len) < 0) ++ goto out; ++ ++ if (wpa_ft_rrb_lin(tlvs_auth, NULL, NULL, &auth, &auth_len) < 0) ++ goto out; ++ ++ *packet_len = sizeof(u16) + auth_len + plain_len; ++ if (key) ++ *packet_len += AES_BLOCK_SIZE; ++#define RRB_MIN_MSG_LEN 64 ++ if (*packet_len < RRB_MIN_MSG_LEN) { ++ pad_len = RRB_MIN_MSG_LEN - *packet_len; ++ if (pad_len < sizeof(struct ft_rrb_tlv)) ++ pad_len = sizeof(struct ft_rrb_tlv); ++ wpa_printf(MSG_DEBUG, ++ "FT: Pad message to minimum Ethernet frame length (%d --> %d)", ++ (int) *packet_len, (int) (*packet_len + pad_len)); ++ *packet_len += pad_len; ++ tmp = os_realloc(auth, auth_len + pad_len); ++ if (!tmp) ++ goto out; ++ auth = tmp; ++ pos = auth + auth_len; ++ WPA_PUT_LE16(pos, FT_RRB_LAST_EMPTY); ++ pos += 2; ++ WPA_PUT_LE16(pos, pad_len - sizeof(struct ft_rrb_tlv)); ++ pos += 2; ++ os_memset(pos, 0, pad_len - sizeof(struct ft_rrb_tlv)); ++ auth_len += pad_len; ++ ++ } ++ *packet = os_zalloc(*packet_len); ++ if (!*packet) ++ goto out; ++ ++ pos = *packet; ++ WPA_PUT_LE16(pos, auth_len); ++ pos += 2; ++ os_memcpy(pos, auth, auth_len); ++ pos += auth_len; ++ if (wpa_ft_rrb_encrypt(key, key_len, plain, plain_len, auth, ++ auth_len, src_addr, type, pos) < 0) ++ goto out; ++ wpa_hexdump(MSG_MSGDUMP, "FT: RRB frame payload", *packet, *packet_len); ++ ++ ret = 0; ++ ++out: ++ bin_clear_free(plain, plain_len); ++ os_free(auth); ++ ++ if (ret) { ++ wpa_printf(MSG_ERROR, "FT: Failed to build RRB-OUI message"); ++ os_free(*packet); ++ *packet = NULL; ++ *packet_len = 0; ++ } ++ ++ return ret; ++} ++ ++ ++#define RRB_GET_SRC(srcfield, type, field, txt, checklength) do { \ ++ if (wpa_ft_rrb_get_tlv(srcfield, srcfield##_len, type, \ ++ &f_##field##_len, &f_##field) < 0 || \ ++ (checklength > 0 && ((size_t) checklength) != f_##field##_len)) { \ ++ wpa_printf(MSG_INFO, "FT: Missing required " #field \ ++ " in %s from " MACSTR, txt, MAC2STR(src_addr)); \ ++ wpa_ft_rrb_dump(srcfield, srcfield##_len); \ ++ goto out; \ ++ } \ ++} while (0) ++ ++#define RRB_GET(type, field, txt, checklength) \ ++ RRB_GET_SRC(plain, type, field, txt, checklength) ++#define RRB_GET_AUTH(type, field, txt, checklength) \ ++ RRB_GET_SRC(auth, type, field, txt, checklength) ++ ++#define RRB_GET_OPTIONAL_SRC(srcfield, type, field, txt, checklength) do { \ ++ if (wpa_ft_rrb_get_tlv(srcfield, srcfield##_len, type, \ ++ &f_##field##_len, &f_##field) < 0 || \ ++ (checklength > 0 && ((size_t) checklength) != f_##field##_len)) { \ ++ wpa_printf(MSG_DEBUG, "FT: Missing optional " #field \ ++ " in %s from " MACSTR, txt, MAC2STR(src_addr)); \ ++ f_##field##_len = 0; \ ++ f_##field = NULL; \ ++ } \ ++} while (0) ++ ++#define RRB_GET_OPTIONAL(type, field, txt, checklength) \ ++ RRB_GET_OPTIONAL_SRC(plain, type, field, txt, checklength) ++#define RRB_GET_OPTIONAL_AUTH(type, field, txt, checklength) \ ++ RRB_GET_OPTIONAL_SRC(auth, type, field, txt, checklength) ++ + 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) ++ 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); ++ return wpa_auth->cb->send_ether(wpa_auth->cb_ctx, dst, ETH_P_RRB, ++ data, data_len); + } + + ++static int wpa_ft_rrb_oui_send(struct wpa_authenticator *wpa_auth, ++ const u8 *dst, u8 oui_suffix, ++ const u8 *data, size_t data_len) ++{ ++ if (!wpa_auth->cb->send_oui) ++ return -1; ++ wpa_printf(MSG_DEBUG, "FT: RRB-OUI type %u send to " MACSTR " (len=%u)", ++ oui_suffix, MAC2STR(dst), (unsigned int) data_len); ++ return wpa_auth->cb->send_oui(wpa_auth->cb_ctx, dst, oui_suffix, 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) ++ 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); ++ return wpa_auth->cb->send_ft_action(wpa_auth->cb_ctx, dst, ++ data, data_len); + } + + ++static const u8 * wpa_ft_get_psk(struct wpa_authenticator *wpa_auth, ++ const u8 *addr, const u8 *p2p_dev_addr, ++ const u8 *prev_psk) ++{ ++ if (wpa_auth->cb->get_psk == NULL) ++ return NULL; ++ return wpa_auth->cb->get_psk(wpa_auth->cb_ctx, addr, p2p_dev_addr, ++ prev_psk, NULL, NULL); ++} ++ ++ + 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) ++ if (wpa_auth->cb->add_sta == NULL) + return NULL; +- return wpa_auth->cb.add_sta(wpa_auth->cb.ctx, sta_addr); ++ return wpa_auth->cb->add_sta(wpa_auth->cb_ctx, sta_addr); + } + + ++static int wpa_ft_set_vlan(struct wpa_authenticator *wpa_auth, ++ const u8 *sta_addr, struct vlan_description *vlan) ++{ ++ if (!wpa_auth->cb->set_vlan) ++ return -1; ++ return wpa_auth->cb->set_vlan(wpa_auth->cb_ctx, sta_addr, vlan); ++} ++ ++ ++static int wpa_ft_get_vlan(struct wpa_authenticator *wpa_auth, ++ const u8 *sta_addr, struct vlan_description *vlan) ++{ ++ if (!wpa_auth->cb->get_vlan) ++ return -1; ++ return wpa_auth->cb->get_vlan(wpa_auth->cb_ctx, sta_addr, vlan); ++} ++ ++ ++static int ++wpa_ft_set_identity(struct wpa_authenticator *wpa_auth, const u8 *sta_addr, ++ const u8 *identity, size_t identity_len) ++{ ++ if (!wpa_auth->cb->set_identity) ++ return -1; ++ return wpa_auth->cb->set_identity(wpa_auth->cb_ctx, sta_addr, identity, ++ identity_len); ++} ++ ++ ++static size_t ++wpa_ft_get_identity(struct wpa_authenticator *wpa_auth, const u8 *sta_addr, ++ const u8 **buf) ++{ ++ *buf = NULL; ++ if (!wpa_auth->cb->get_identity) ++ return 0; ++ return wpa_auth->cb->get_identity(wpa_auth->cb_ctx, sta_addr, buf); ++} ++ ++ ++static int ++wpa_ft_set_radius_cui(struct wpa_authenticator *wpa_auth, const u8 *sta_addr, ++ const u8 *radius_cui, size_t radius_cui_len) ++{ ++ if (!wpa_auth->cb->set_radius_cui) ++ return -1; ++ return wpa_auth->cb->set_radius_cui(wpa_auth->cb_ctx, sta_addr, ++ radius_cui, radius_cui_len); ++} ++ ++ ++static size_t ++wpa_ft_get_radius_cui(struct wpa_authenticator *wpa_auth, const u8 *sta_addr, ++ const u8 **buf) ++{ ++ *buf = NULL; ++ if (!wpa_auth->cb->get_radius_cui) ++ return 0; ++ return wpa_auth->cb->get_radius_cui(wpa_auth->cb_ctx, sta_addr, buf); ++} ++ ++ ++static void ++wpa_ft_set_session_timeout(struct wpa_authenticator *wpa_auth, ++ const u8 *sta_addr, int session_timeout) ++{ ++ if (!wpa_auth->cb->set_session_timeout) ++ return; ++ wpa_auth->cb->set_session_timeout(wpa_auth->cb_ctx, sta_addr, ++ session_timeout); ++} ++ ++ ++static int ++wpa_ft_get_session_timeout(struct wpa_authenticator *wpa_auth, ++ const u8 *sta_addr) ++{ ++ if (!wpa_auth->cb->get_session_timeout) ++ return 0; ++ return wpa_auth->cb->get_session_timeout(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) { ++ 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); ++ return wpa_auth->cb->add_tspec(wpa_auth->cb_ctx, sta_addr, tspec_ie, ++ tspec_ielen); + } + + ++#ifdef CONFIG_OCV ++static int wpa_channel_info(struct wpa_authenticator *wpa_auth, ++ struct wpa_channel_info *ci) ++{ ++ if (!wpa_auth->cb->channel_info) ++ return -1; ++ return wpa_auth->cb->channel_info(wpa_auth->cb_ctx, ci); ++} ++#endif /* CONFIG_OCV */ ++ ++ + int wpa_write_mdie(struct wpa_auth_config *conf, u8 *buf, size_t len) + { + u8 *pos = buf; +@@ -93,16 +803,17 @@ + } + + +-int wpa_write_ftie(struct wpa_auth_config *conf, const u8 *r0kh_id, +- size_t r0kh_id_len, ++int wpa_write_ftie(struct wpa_auth_config *conf, int use_sha384, ++ 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; ++ size_t hdrlen = use_sha384 ? sizeof(struct rsn_ftie_sha384) : ++ sizeof(struct rsn_ftie); + +- if (len < 2 + sizeof(*hdr) + 2 + FT_R1KH_ID_LEN + 2 + r0kh_id_len + ++ if (len < 2 + hdrlen + 2 + FT_R1KH_ID_LEN + 2 + r0kh_id_len + + subelem_len) + return -1; + +@@ -109,15 +820,28 @@ + *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); ++ if (use_sha384) { ++ struct rsn_ftie_sha384 *hdr = (struct rsn_ftie_sha384 *) 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); ++ } else { ++ struct rsn_ftie *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; +@@ -142,35 +866,434 @@ + } + + ++/* A packet to be handled after seq response */ ++struct ft_remote_item { ++ struct dl_list list; ++ ++ u8 nonce[FT_RRB_NONCE_LEN]; ++ struct os_reltime nonce_ts; ++ ++ u8 src_addr[ETH_ALEN]; ++ u8 *enc; ++ size_t enc_len; ++ u8 *auth; ++ size_t auth_len; ++ int (*cb)(struct wpa_authenticator *wpa_auth, ++ const u8 *src_addr, ++ const u8 *enc, size_t enc_len, ++ const u8 *auth, size_t auth_len, ++ int no_defer); ++}; ++ ++ ++static void wpa_ft_rrb_seq_free(struct ft_remote_item *item) ++{ ++ eloop_cancel_timeout(wpa_ft_rrb_seq_timeout, ELOOP_ALL_CTX, item); ++ dl_list_del(&item->list); ++ bin_clear_free(item->enc, item->enc_len); ++ os_free(item->auth); ++ os_free(item); ++} ++ ++ ++static void wpa_ft_rrb_seq_flush(struct wpa_authenticator *wpa_auth, ++ struct ft_remote_seq *rkh_seq, int cb) ++{ ++ struct ft_remote_item *item, *n; ++ ++ dl_list_for_each_safe(item, n, &rkh_seq->rx.queue, ++ struct ft_remote_item, list) { ++ if (cb && item->cb) ++ item->cb(wpa_auth, item->src_addr, item->enc, ++ item->enc_len, item->auth, item->auth_len, 1); ++ wpa_ft_rrb_seq_free(item); ++ } ++} ++ ++ ++static void wpa_ft_rrb_seq_timeout(void *eloop_ctx, void *timeout_ctx) ++{ ++ struct ft_remote_item *item = timeout_ctx; ++ ++ wpa_ft_rrb_seq_free(item); ++} ++ ++ ++static int ++wpa_ft_rrb_seq_req(struct wpa_authenticator *wpa_auth, ++ struct ft_remote_seq *rkh_seq, const u8 *src_addr, ++ const u8 *f_r0kh_id, size_t f_r0kh_id_len, ++ const u8 *f_r1kh_id, const u8 *key, size_t key_len, ++ const u8 *enc, size_t enc_len, ++ const u8 *auth, size_t auth_len, ++ int (*cb)(struct wpa_authenticator *wpa_auth, ++ const u8 *src_addr, ++ const u8 *enc, size_t enc_len, ++ const u8 *auth, size_t auth_len, ++ int no_defer)) ++{ ++ struct ft_remote_item *item = NULL; ++ u8 *packet = NULL; ++ size_t packet_len; ++ struct tlv_list seq_req_auth[] = { ++ { .type = FT_RRB_NONCE, .len = FT_RRB_NONCE_LEN, ++ .data = NULL /* to be filled: item->nonce */ }, ++ { .type = FT_RRB_R0KH_ID, .len = f_r0kh_id_len, ++ .data = f_r0kh_id }, ++ { .type = FT_RRB_R1KH_ID, .len = FT_R1KH_ID_LEN, ++ .data = f_r1kh_id }, ++ { .type = FT_RRB_LAST_EMPTY, .len = 0, .data = NULL }, ++ }; ++ ++ if (dl_list_len(&rkh_seq->rx.queue) >= ftRRBmaxQueueLen) { ++ wpa_printf(MSG_DEBUG, "FT: Sequence number queue too long"); ++ goto err; ++ } ++ ++ wpa_printf(MSG_DEBUG, "FT: Send out sequence number request to " MACSTR, ++ MAC2STR(src_addr)); ++ item = os_zalloc(sizeof(*item)); ++ if (!item) ++ goto err; ++ ++ os_memcpy(item->src_addr, src_addr, ETH_ALEN); ++ item->cb = cb; ++ ++ if (random_get_bytes(item->nonce, FT_RRB_NONCE_LEN) < 0) { ++ wpa_printf(MSG_DEBUG, "FT: Seq num nonce: out of random bytes"); ++ goto err; ++ } ++ ++ if (os_get_reltime(&item->nonce_ts) < 0) ++ goto err; ++ ++ if (enc && enc_len > 0) { ++ item->enc = os_memdup(enc, enc_len); ++ item->enc_len = enc_len; ++ if (!item->enc) ++ goto err; ++ } ++ ++ if (auth && auth_len > 0) { ++ item->auth = os_memdup(auth, auth_len); ++ item->auth_len = auth_len; ++ if (!item->auth) ++ goto err; ++ } ++ ++ eloop_register_timeout(ftRRBseqTimeout, 0, wpa_ft_rrb_seq_timeout, ++ wpa_auth, item); ++ ++ seq_req_auth[0].data = item->nonce; ++ ++ if (wpa_ft_rrb_build(key, key_len, NULL, NULL, seq_req_auth, NULL, ++ wpa_auth->addr, FT_PACKET_R0KH_R1KH_SEQ_REQ, ++ &packet, &packet_len) < 0) { ++ item = NULL; /* some other seq resp might still accept this */ ++ goto err; ++ } ++ ++ dl_list_add(&rkh_seq->rx.queue, &item->list); ++ ++ wpa_ft_rrb_oui_send(wpa_auth, src_addr, FT_PACKET_R0KH_R1KH_SEQ_REQ, ++ packet, packet_len); ++ ++ os_free(packet); ++ ++ return 0; ++err: ++ wpa_printf(MSG_DEBUG, "FT: Failed to send sequence number request"); ++ if (item) { ++ os_free(item->auth); ++ bin_clear_free(item->enc, item->enc_len); ++ os_free(item); ++ } ++ ++ return -1; ++} ++ ++ ++#define FT_RRB_SEQ_OK 0 ++#define FT_RRB_SEQ_DROP 1 ++#define FT_RRB_SEQ_DEFER 2 ++ ++static int ++wpa_ft_rrb_seq_chk(struct ft_remote_seq *rkh_seq, const u8 *src_addr, ++ const u8 *enc, size_t enc_len, ++ const u8 *auth, size_t auth_len, ++ const char *msgtype, int no_defer) ++{ ++ const u8 *f_seq; ++ size_t f_seq_len; ++ const struct ft_rrb_seq *msg_both; ++ u32 msg_seq, msg_off, rkh_off; ++ struct os_reltime now; ++ unsigned int i; ++ ++ RRB_GET_AUTH(FT_RRB_SEQ, seq, msgtype, sizeof(*msg_both)); ++ wpa_hexdump(MSG_DEBUG, "FT: sequence number", f_seq, f_seq_len); ++ msg_both = (const struct ft_rrb_seq *) f_seq; ++ ++ if (rkh_seq->rx.num_last == 0) { ++ /* first packet from remote */ ++ goto defer; ++ } ++ ++ if (le_to_host32(msg_both->dom) != rkh_seq->rx.dom) { ++ /* remote might have rebooted */ ++ goto defer; ++ } ++ ++ if (os_get_reltime(&now) == 0) { ++ u32 msg_ts_now_remote, msg_ts_off; ++ struct os_reltime now_remote; ++ ++ os_reltime_sub(&now, &rkh_seq->rx.time_offset, &now_remote); ++ msg_ts_now_remote = now_remote.sec; ++ msg_ts_off = le_to_host32(msg_both->ts) - ++ (msg_ts_now_remote - ftRRBseqTimeout); ++ if (msg_ts_off > 2 * ftRRBseqTimeout) ++ goto defer; ++ } ++ ++ msg_seq = le_to_host32(msg_both->seq); ++ rkh_off = rkh_seq->rx.last[rkh_seq->rx.offsetidx]; ++ msg_off = msg_seq - rkh_off; ++ if (msg_off > 0xC0000000) ++ goto out; /* too old message, drop it */ ++ ++ if (msg_off <= 0x40000000) { ++ for (i = 0; i < rkh_seq->rx.num_last; i++) { ++ if (rkh_seq->rx.last[i] == msg_seq) ++ goto out; /* duplicate message, drop it */ ++ } ++ ++ return FT_RRB_SEQ_OK; ++ } ++ ++defer: ++ if (no_defer) ++ goto out; ++ ++ wpa_printf(MSG_DEBUG, "FT: Possibly invalid sequence number in %s from " ++ MACSTR, msgtype, MAC2STR(src_addr)); ++ ++ return FT_RRB_SEQ_DEFER; ++out: ++ wpa_printf(MSG_DEBUG, "FT: Invalid sequence number in %s from " MACSTR, ++ msgtype, MAC2STR(src_addr)); ++ ++ return FT_RRB_SEQ_DROP; ++} ++ ++ ++static void ++wpa_ft_rrb_seq_accept(struct wpa_authenticator *wpa_auth, ++ struct ft_remote_seq *rkh_seq, const u8 *src_addr, ++ const u8 *auth, size_t auth_len, ++ const char *msgtype) ++{ ++ const u8 *f_seq; ++ size_t f_seq_len; ++ const struct ft_rrb_seq *msg_both; ++ u32 msg_seq, msg_off, min_off, rkh_off; ++ int minidx = 0; ++ unsigned int i; ++ ++ RRB_GET_AUTH(FT_RRB_SEQ, seq, msgtype, sizeof(*msg_both)); ++ msg_both = (const struct ft_rrb_seq *) f_seq; ++ ++ msg_seq = le_to_host32(msg_both->seq); ++ ++ if (rkh_seq->rx.num_last < FT_REMOTE_SEQ_BACKLOG) { ++ rkh_seq->rx.last[rkh_seq->rx.num_last] = msg_seq; ++ rkh_seq->rx.num_last++; ++ return; ++ } ++ ++ rkh_off = rkh_seq->rx.last[rkh_seq->rx.offsetidx]; ++ for (i = 0; i < rkh_seq->rx.num_last; i++) { ++ msg_off = rkh_seq->rx.last[i] - rkh_off; ++ min_off = rkh_seq->rx.last[minidx] - rkh_off; ++ if (msg_off < min_off && i != rkh_seq->rx.offsetidx) ++ minidx = i; ++ } ++ rkh_seq->rx.last[rkh_seq->rx.offsetidx] = msg_seq; ++ rkh_seq->rx.offsetidx = minidx; ++ ++ return; ++out: ++ /* RRB_GET_AUTH should never fail here as ++ * wpa_ft_rrb_seq_chk() verified FT_RRB_SEQ presence. */ ++ wpa_printf(MSG_ERROR, "FT: %s() failed", __func__); ++} ++ ++ ++static int wpa_ft_new_seq(struct ft_remote_seq *rkh_seq, ++ struct ft_rrb_seq *f_seq) ++{ ++ struct os_reltime now; ++ ++ if (os_get_reltime(&now) < 0) ++ return -1; ++ ++ if (!rkh_seq->tx.dom) { ++ if (random_get_bytes((u8 *) &rkh_seq->tx.seq, ++ sizeof(rkh_seq->tx.seq))) { ++ wpa_printf(MSG_ERROR, ++ "FT: Failed to get random data for sequence number initialization"); ++ rkh_seq->tx.seq = now.usec; ++ } ++ if (random_get_bytes((u8 *) &rkh_seq->tx.dom, ++ sizeof(rkh_seq->tx.dom))) { ++ wpa_printf(MSG_ERROR, ++ "FT: Failed to get random data for sequence number initialization"); ++ rkh_seq->tx.dom = now.usec; ++ } ++ rkh_seq->tx.dom |= 1; ++ } ++ ++ f_seq->dom = host_to_le32(rkh_seq->tx.dom); ++ f_seq->seq = host_to_le32(rkh_seq->tx.seq); ++ f_seq->ts = host_to_le32(now.sec); ++ ++ rkh_seq->tx.seq++; ++ ++ return 0; ++} ++ ++ + struct wpa_ft_pmk_r0_sa { +- struct wpa_ft_pmk_r0_sa *next; +- u8 pmk_r0[PMK_LEN]; ++ struct dl_list list; ++ u8 pmk_r0[PMK_LEN_MAX]; ++ size_t pmk_r0_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 */ ++ struct vlan_description *vlan; ++ os_time_t expiration; /* 0 for no expiration */ ++ u8 *identity; ++ size_t identity_len; ++ u8 *radius_cui; ++ size_t radius_cui_len; ++ os_time_t session_timeout; /* 0 for no expiration */ ++ /* TODO: radius_class, EAP type */ + int pmk_r1_pushed; + }; + + struct wpa_ft_pmk_r1_sa { +- struct wpa_ft_pmk_r1_sa *next; +- u8 pmk_r1[PMK_LEN]; ++ struct dl_list list; ++ u8 pmk_r1[PMK_LEN_MAX]; ++ size_t pmk_r1_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 vlan_description *vlan; ++ u8 *identity; ++ size_t identity_len; ++ u8 *radius_cui; ++ size_t radius_cui_len; ++ os_time_t session_timeout; /* 0 for no expiration */ ++ /* TODO: radius_class, EAP type */ + }; + + struct wpa_ft_pmk_cache { +- struct wpa_ft_pmk_r0_sa *pmk_r0; +- struct wpa_ft_pmk_r1_sa *pmk_r1; ++ struct dl_list pmk_r0; /* struct wpa_ft_pmk_r0_sa */ ++ struct dl_list pmk_r1; /* struct wpa_ft_pmk_r1_sa */ + }; + ++ ++static void wpa_ft_expire_pmk_r0(void *eloop_ctx, void *timeout_ctx); ++static void wpa_ft_expire_pmk_r1(void *eloop_ctx, void *timeout_ctx); ++ ++ ++static void wpa_ft_free_pmk_r0(struct wpa_ft_pmk_r0_sa *r0) ++{ ++ if (!r0) ++ return; ++ ++ dl_list_del(&r0->list); ++ eloop_cancel_timeout(wpa_ft_expire_pmk_r0, r0, NULL); ++ ++ os_memset(r0->pmk_r0, 0, PMK_LEN_MAX); ++ os_free(r0->vlan); ++ os_free(r0->identity); ++ os_free(r0->radius_cui); ++ os_free(r0); ++} ++ ++ ++static void wpa_ft_expire_pmk_r0(void *eloop_ctx, void *timeout_ctx) ++{ ++ struct wpa_ft_pmk_r0_sa *r0 = eloop_ctx; ++ struct os_reltime now; ++ int expires_in; ++ int session_timeout; ++ ++ os_get_reltime(&now); ++ ++ if (!r0) ++ return; ++ ++ expires_in = r0->expiration - now.sec; ++ session_timeout = r0->session_timeout - now.sec; ++ /* conditions to remove from cache: ++ * a) r0->expiration is set and hit ++ * -or- ++ * b) r0->session_timeout is set and hit ++ */ ++ if ((!r0->expiration || expires_in > 0) && ++ (!r0->session_timeout || session_timeout > 0)) { ++ wpa_printf(MSG_ERROR, ++ "FT: %s() called for non-expired entry %p", ++ __func__, r0); ++ eloop_cancel_timeout(wpa_ft_expire_pmk_r0, r0, NULL); ++ if (r0->expiration && expires_in > 0) ++ eloop_register_timeout(expires_in + 1, 0, ++ wpa_ft_expire_pmk_r0, r0, NULL); ++ if (r0->session_timeout && session_timeout > 0) ++ eloop_register_timeout(session_timeout + 1, 0, ++ wpa_ft_expire_pmk_r0, r0, NULL); ++ return; ++ } ++ ++ wpa_ft_free_pmk_r0(r0); ++} ++ ++ ++static void wpa_ft_free_pmk_r1(struct wpa_ft_pmk_r1_sa *r1) ++{ ++ if (!r1) ++ return; ++ ++ dl_list_del(&r1->list); ++ eloop_cancel_timeout(wpa_ft_expire_pmk_r1, r1, NULL); ++ ++ os_memset(r1->pmk_r1, 0, PMK_LEN_MAX); ++ os_free(r1->vlan); ++ os_free(r1->identity); ++ os_free(r1->radius_cui); ++ os_free(r1); ++} ++ ++ ++static void wpa_ft_expire_pmk_r1(void *eloop_ctx, void *timeout_ctx) ++{ ++ struct wpa_ft_pmk_r1_sa *r1 = eloop_ctx; ++ ++ wpa_ft_free_pmk_r1(r1); ++} ++ ++ + struct wpa_ft_pmk_cache * wpa_ft_pmk_cache_init(void) + { + struct wpa_ft_pmk_cache *cache; + + cache = os_zalloc(sizeof(*cache)); ++ if (cache) { ++ dl_list_init(&cache->pmk_r0); ++ dl_list_init(&cache->pmk_r1); ++ } + + return cache; + } +@@ -181,21 +1304,13 @@ + 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); +- } ++ dl_list_for_each_safe(r0, r0prev, &cache->pmk_r0, ++ struct wpa_ft_pmk_r0_sa, list) ++ wpa_ft_free_pmk_r0(r0); + +- r1 = cache->pmk_r1; +- while (r1) { +- r1prev = r1; +- r1 = r1->next; +- os_memset(r1prev->pmk_r1, 0, PMK_LEN); +- os_free(r1prev); +- } ++ dl_list_for_each_safe(r1, r1prev, &cache->pmk_r1, ++ struct wpa_ft_pmk_r1_sa, list) ++ wpa_ft_free_pmk_r1(r1); + + os_free(cache); + } +@@ -203,24 +1318,63 @@ + + 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) ++ size_t pmk_r0_len, ++ const u8 *pmk_r0_name, int pairwise, ++ const struct vlan_description *vlan, ++ int expires_in, int session_timeout, ++ const u8 *identity, size_t identity_len, ++ const u8 *radius_cui, size_t radius_cui_len) + { + struct wpa_ft_pmk_cache *cache = wpa_auth->ft_pmk_cache; + struct wpa_ft_pmk_r0_sa *r0; ++ struct os_reltime now; + +- /* TODO: add expiration and limit on number of entries in cache */ ++ /* TODO: add limit on number of entries in cache */ ++ os_get_reltime(&now); + + r0 = os_zalloc(sizeof(*r0)); + if (r0 == NULL) + return -1; + +- os_memcpy(r0->pmk_r0, pmk_r0, PMK_LEN); ++ os_memcpy(r0->pmk_r0, pmk_r0, pmk_r0_len); ++ r0->pmk_r0_len = pmk_r0_len; + os_memcpy(r0->pmk_r0_name, pmk_r0_name, WPA_PMK_NAME_LEN); + os_memcpy(r0->spa, spa, ETH_ALEN); + r0->pairwise = pairwise; ++ if (expires_in > 0) ++ r0->expiration = now.sec + expires_in; ++ if (vlan && vlan->notempty) { ++ r0->vlan = os_zalloc(sizeof(*vlan)); ++ if (!r0->vlan) { ++ bin_clear_free(r0, sizeof(*r0)); ++ return -1; ++ } ++ *r0->vlan = *vlan; ++ } ++ if (identity) { ++ r0->identity = os_malloc(identity_len); ++ if (r0->identity) { ++ os_memcpy(r0->identity, identity, identity_len); ++ r0->identity_len = identity_len; ++ } ++ } ++ if (radius_cui) { ++ r0->radius_cui = os_malloc(radius_cui_len); ++ if (r0->radius_cui) { ++ os_memcpy(r0->radius_cui, radius_cui, radius_cui_len); ++ r0->radius_cui_len = radius_cui_len; ++ } ++ } ++ if (session_timeout > 0) ++ r0->session_timeout = now.sec + session_timeout; + +- r0->next = cache->pmk_r0; +- cache->pmk_r0 = r0; ++ dl_list_add(&cache->pmk_r0, &r0->list); ++ if (expires_in > 0) ++ eloop_register_timeout(expires_in + 1, 0, wpa_ft_expire_pmk_r0, ++ r0, NULL); ++ if (session_timeout > 0) ++ eloop_register_timeout(session_timeout + 1, 0, ++ wpa_ft_expire_pmk_r0, r0, NULL); + + return 0; + } +@@ -228,25 +1382,23 @@ + + 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) ++ const struct wpa_ft_pmk_r0_sa **r0_out) + { + struct wpa_ft_pmk_cache *cache = wpa_auth->ft_pmk_cache; + struct wpa_ft_pmk_r0_sa *r0; ++ struct os_reltime now; + +- r0 = cache->pmk_r0; +- while (r0) { ++ os_get_reltime(&now); ++ dl_list_for_each(r0, &cache->pmk_r0, struct wpa_ft_pmk_r0_sa, list) { + if (os_memcmp(r0->spa, spa, ETH_ALEN) == 0 && + os_memcmp_const(r0->pmk_r0_name, pmk_r0_name, + WPA_PMK_NAME_LEN) == 0) { +- os_memcpy(pmk_r0, r0->pmk_r0, PMK_LEN); +- if (pairwise) +- *pairwise = r0->pairwise; ++ *r0_out = r0; + return 0; + } +- +- r0 = r0->next; + } + ++ *r0_out = NULL; + return -1; + } + +@@ -253,25 +1405,67 @@ + + 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) ++ size_t pmk_r1_len, ++ const u8 *pmk_r1_name, int pairwise, ++ const struct vlan_description *vlan, ++ int expires_in, int session_timeout, ++ const u8 *identity, size_t identity_len, ++ const u8 *radius_cui, size_t radius_cui_len) + { + struct wpa_ft_pmk_cache *cache = wpa_auth->ft_pmk_cache; ++ int max_expires_in = wpa_auth->conf.r1_max_key_lifetime; + struct wpa_ft_pmk_r1_sa *r1; ++ struct os_reltime now; + +- /* TODO: add expiration and limit on number of entries in cache */ ++ /* TODO: limit on number of entries in cache */ ++ os_get_reltime(&now); + ++ if (max_expires_in && (max_expires_in < expires_in || expires_in == 0)) ++ expires_in = max_expires_in; ++ + r1 = os_zalloc(sizeof(*r1)); + if (r1 == NULL) + return -1; + +- os_memcpy(r1->pmk_r1, pmk_r1, PMK_LEN); ++ os_memcpy(r1->pmk_r1, pmk_r1, pmk_r1_len); ++ r1->pmk_r1_len = pmk_r1_len; + os_memcpy(r1->pmk_r1_name, pmk_r1_name, WPA_PMK_NAME_LEN); + os_memcpy(r1->spa, spa, ETH_ALEN); + r1->pairwise = pairwise; ++ if (vlan && vlan->notempty) { ++ r1->vlan = os_zalloc(sizeof(*vlan)); ++ if (!r1->vlan) { ++ bin_clear_free(r1, sizeof(*r1)); ++ return -1; ++ } ++ *r1->vlan = *vlan; ++ } ++ if (identity) { ++ r1->identity = os_malloc(identity_len); ++ if (r1->identity) { ++ os_memcpy(r1->identity, identity, identity_len); ++ r1->identity_len = identity_len; ++ } ++ } ++ if (radius_cui) { ++ r1->radius_cui = os_malloc(radius_cui_len); ++ if (r1->radius_cui) { ++ os_memcpy(r1->radius_cui, radius_cui, radius_cui_len); ++ r1->radius_cui_len = radius_cui_len; ++ } ++ } ++ if (session_timeout > 0) ++ r1->session_timeout = now.sec + session_timeout; + +- r1->next = cache->pmk_r1; +- cache->pmk_r1 = r1; ++ dl_list_add(&cache->pmk_r1, &r1->list); + ++ if (expires_in > 0) ++ eloop_register_timeout(expires_in + 1, 0, wpa_ft_expire_pmk_r1, ++ r1, NULL); ++ if (session_timeout > 0) ++ eloop_register_timeout(session_timeout + 1, 0, ++ wpa_ft_expire_pmk_r1, r1, NULL); ++ + return 0; + } + +@@ -278,23 +1472,47 @@ + + 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) ++ u8 *pmk_r1, size_t *pmk_r1_len, int *pairwise, ++ struct vlan_description *vlan, ++ const u8 **identity, size_t *identity_len, ++ const u8 **radius_cui, size_t *radius_cui_len, ++ int *session_timeout) + { + struct wpa_ft_pmk_cache *cache = wpa_auth->ft_pmk_cache; + struct wpa_ft_pmk_r1_sa *r1; ++ struct os_reltime now; + +- r1 = cache->pmk_r1; +- while (r1) { ++ os_get_reltime(&now); ++ ++ dl_list_for_each(r1, &cache->pmk_r1, struct wpa_ft_pmk_r1_sa, list) { + if (os_memcmp(r1->spa, spa, ETH_ALEN) == 0 && + os_memcmp_const(r1->pmk_r1_name, pmk_r1_name, + WPA_PMK_NAME_LEN) == 0) { +- os_memcpy(pmk_r1, r1->pmk_r1, PMK_LEN); ++ os_memcpy(pmk_r1, r1->pmk_r1, r1->pmk_r1_len); ++ *pmk_r1_len = r1->pmk_r1_len; + if (pairwise) + *pairwise = r1->pairwise; ++ if (vlan && r1->vlan) ++ *vlan = *r1->vlan; ++ if (vlan && !r1->vlan) ++ os_memset(vlan, 0, sizeof(*vlan)); ++ if (identity && identity_len) { ++ *identity = r1->identity; ++ *identity_len = r1->identity_len; ++ } ++ if (radius_cui && radius_cui_len) { ++ *radius_cui = r1->radius_cui; ++ *radius_cui_len = r1->radius_cui_len; ++ } ++ if (session_timeout && r1->session_timeout > now.sec) ++ *session_timeout = r1->session_timeout - ++ now.sec; ++ else if (session_timeout && r1->session_timeout) ++ *session_timeout = 1; ++ else if (session_timeout) ++ *session_timeout = 0; + return 0; + } +- +- r1 = r1->next; + } + + return -1; +@@ -301,20 +1519,463 @@ + } + + ++static int wpa_ft_rrb_init_r0kh_seq(struct ft_remote_r0kh *r0kh) ++{ ++ if (r0kh->seq) ++ return 0; ++ ++ r0kh->seq = os_zalloc(sizeof(*r0kh->seq)); ++ if (!r0kh->seq) { ++ wpa_printf(MSG_DEBUG, "FT: Failed to allocate r0kh->seq"); ++ return -1; ++ } ++ ++ dl_list_init(&r0kh->seq->rx.queue); ++ ++ return 0; ++} ++ ++ ++static void wpa_ft_rrb_lookup_r0kh(struct wpa_authenticator *wpa_auth, ++ const u8 *f_r0kh_id, size_t f_r0kh_id_len, ++ struct ft_remote_r0kh **r0kh_out, ++ struct ft_remote_r0kh **r0kh_wildcard) ++{ ++ struct ft_remote_r0kh *r0kh; ++ ++ *r0kh_wildcard = NULL; ++ *r0kh_out = NULL; ++ ++ if (wpa_auth->conf.r0kh_list) ++ r0kh = *wpa_auth->conf.r0kh_list; ++ else ++ r0kh = NULL; ++ for (; r0kh; r0kh = r0kh->next) { ++ if (r0kh->id_len == 1 && r0kh->id[0] == '*') ++ *r0kh_wildcard = r0kh; ++ if (f_r0kh_id && r0kh->id_len == f_r0kh_id_len && ++ os_memcmp_const(f_r0kh_id, r0kh->id, f_r0kh_id_len) == 0) ++ *r0kh_out = r0kh; ++ } ++ ++ if (!*r0kh_out && !*r0kh_wildcard) ++ wpa_printf(MSG_DEBUG, "FT: No matching R0KH found"); ++ ++ if (*r0kh_out && wpa_ft_rrb_init_r0kh_seq(*r0kh_out) < 0) ++ *r0kh_out = NULL; ++} ++ ++ ++static int wpa_ft_rrb_init_r1kh_seq(struct ft_remote_r1kh *r1kh) ++{ ++ if (r1kh->seq) ++ return 0; ++ ++ r1kh->seq = os_zalloc(sizeof(*r1kh->seq)); ++ if (!r1kh->seq) { ++ wpa_printf(MSG_DEBUG, "FT: Failed to allocate r1kh->seq"); ++ return -1; ++ } ++ ++ dl_list_init(&r1kh->seq->rx.queue); ++ ++ return 0; ++} ++ ++ ++static void wpa_ft_rrb_lookup_r1kh(struct wpa_authenticator *wpa_auth, ++ const u8 *f_r1kh_id, ++ struct ft_remote_r1kh **r1kh_out, ++ struct ft_remote_r1kh **r1kh_wildcard) ++{ ++ struct ft_remote_r1kh *r1kh; ++ ++ *r1kh_wildcard = NULL; ++ *r1kh_out = NULL; ++ ++ if (wpa_auth->conf.r1kh_list) ++ r1kh = *wpa_auth->conf.r1kh_list; ++ else ++ r1kh = NULL; ++ for (; r1kh; r1kh = r1kh->next) { ++ if (is_zero_ether_addr(r1kh->addr) && ++ is_zero_ether_addr(r1kh->id)) ++ *r1kh_wildcard = r1kh; ++ if (f_r1kh_id && ++ os_memcmp_const(r1kh->id, f_r1kh_id, FT_R1KH_ID_LEN) == 0) ++ *r1kh_out = r1kh; ++ } ++ ++ if (!*r1kh_out && !*r1kh_wildcard) ++ wpa_printf(MSG_DEBUG, "FT: No matching R1KH found"); ++ ++ if (*r1kh_out && wpa_ft_rrb_init_r1kh_seq(*r1kh_out) < 0) ++ *r1kh_out = NULL; ++} ++ ++ ++static int wpa_ft_rrb_check_r0kh(struct wpa_authenticator *wpa_auth, ++ const u8 *f_r0kh_id, size_t f_r0kh_id_len) ++{ ++ if (f_r0kh_id_len != wpa_auth->conf.r0_key_holder_len || ++ os_memcmp_const(f_r0kh_id, wpa_auth->conf.r0_key_holder, ++ f_r0kh_id_len) != 0) ++ return -1; ++ ++ return 0; ++} ++ ++ ++static int wpa_ft_rrb_check_r1kh(struct wpa_authenticator *wpa_auth, ++ const u8 *f_r1kh_id) ++{ ++ if (os_memcmp_const(f_r1kh_id, wpa_auth->conf.r1_key_holder, ++ FT_R1KH_ID_LEN) != 0) ++ return -1; ++ ++ return 0; ++} ++ ++ ++static void wpa_ft_rrb_del_r0kh(void *eloop_ctx, void *timeout_ctx) ++{ ++ struct wpa_authenticator *wpa_auth = eloop_ctx; ++ struct ft_remote_r0kh *r0kh, *prev = NULL; ++ ++ if (!wpa_auth->conf.r0kh_list) ++ return; ++ ++ for (r0kh = *wpa_auth->conf.r0kh_list; r0kh; r0kh = r0kh->next) { ++ if (r0kh == timeout_ctx) ++ break; ++ prev = r0kh; ++ } ++ if (!r0kh) ++ return; ++ if (prev) ++ prev->next = r0kh->next; ++ else ++ *wpa_auth->conf.r0kh_list = r0kh->next; ++ if (r0kh->seq) ++ wpa_ft_rrb_seq_flush(wpa_auth, r0kh->seq, 0); ++ os_free(r0kh->seq); ++ os_free(r0kh); ++} ++ ++ ++static void wpa_ft_rrb_r0kh_replenish(struct wpa_authenticator *wpa_auth, ++ struct ft_remote_r0kh *r0kh, int timeout) ++{ ++ if (timeout > 0) ++ eloop_replenish_timeout(timeout, 0, wpa_ft_rrb_del_r0kh, ++ wpa_auth, r0kh); ++} ++ ++ ++static void wpa_ft_rrb_r0kh_timeout(struct wpa_authenticator *wpa_auth, ++ struct ft_remote_r0kh *r0kh, int timeout) ++{ ++ eloop_cancel_timeout(wpa_ft_rrb_del_r0kh, wpa_auth, r0kh); ++ ++ if (timeout > 0) ++ eloop_register_timeout(timeout, 0, wpa_ft_rrb_del_r0kh, ++ wpa_auth, r0kh); ++} ++ ++ ++static struct ft_remote_r0kh * ++wpa_ft_rrb_add_r0kh(struct wpa_authenticator *wpa_auth, ++ struct ft_remote_r0kh *r0kh_wildcard, ++ const u8 *src_addr, const u8 *r0kh_id, size_t id_len, ++ int timeout) ++{ ++ struct ft_remote_r0kh *r0kh; ++ ++ if (!wpa_auth->conf.r0kh_list) ++ return NULL; ++ ++ r0kh = os_zalloc(sizeof(*r0kh)); ++ if (!r0kh) ++ return NULL; ++ ++ if (src_addr) ++ os_memcpy(r0kh->addr, src_addr, sizeof(r0kh->addr)); ++ ++ if (id_len > FT_R0KH_ID_MAX_LEN) ++ id_len = FT_R0KH_ID_MAX_LEN; ++ os_memcpy(r0kh->id, r0kh_id, id_len); ++ r0kh->id_len = id_len; ++ ++ os_memcpy(r0kh->key, r0kh_wildcard->key, sizeof(r0kh->key)); ++ ++ r0kh->next = *wpa_auth->conf.r0kh_list; ++ *wpa_auth->conf.r0kh_list = r0kh; ++ ++ if (timeout > 0) ++ eloop_register_timeout(timeout, 0, wpa_ft_rrb_del_r0kh, ++ wpa_auth, r0kh); ++ ++ if (wpa_ft_rrb_init_r0kh_seq(r0kh) < 0) ++ return NULL; ++ ++ return r0kh; ++} ++ ++ ++static void wpa_ft_rrb_del_r1kh(void *eloop_ctx, void *timeout_ctx) ++{ ++ struct wpa_authenticator *wpa_auth = eloop_ctx; ++ struct ft_remote_r1kh *r1kh, *prev = NULL; ++ ++ if (!wpa_auth->conf.r1kh_list) ++ return; ++ ++ for (r1kh = *wpa_auth->conf.r1kh_list; r1kh; r1kh = r1kh->next) { ++ if (r1kh == timeout_ctx) ++ break; ++ prev = r1kh; ++ } ++ if (!r1kh) ++ return; ++ if (prev) ++ prev->next = r1kh->next; ++ else ++ *wpa_auth->conf.r1kh_list = r1kh->next; ++ if (r1kh->seq) ++ wpa_ft_rrb_seq_flush(wpa_auth, r1kh->seq, 0); ++ os_free(r1kh->seq); ++ os_free(r1kh); ++} ++ ++ ++static void wpa_ft_rrb_r1kh_replenish(struct wpa_authenticator *wpa_auth, ++ struct ft_remote_r1kh *r1kh, int timeout) ++{ ++ if (timeout > 0) ++ eloop_replenish_timeout(timeout, 0, wpa_ft_rrb_del_r1kh, ++ wpa_auth, r1kh); ++} ++ ++ ++static struct ft_remote_r1kh * ++wpa_ft_rrb_add_r1kh(struct wpa_authenticator *wpa_auth, ++ struct ft_remote_r1kh *r1kh_wildcard, ++ const u8 *src_addr, const u8 *r1kh_id, int timeout) ++{ ++ struct ft_remote_r1kh *r1kh; ++ ++ if (!wpa_auth->conf.r1kh_list) ++ return NULL; ++ ++ r1kh = os_zalloc(sizeof(*r1kh)); ++ if (!r1kh) ++ return NULL; ++ ++ os_memcpy(r1kh->addr, src_addr, sizeof(r1kh->addr)); ++ os_memcpy(r1kh->id, r1kh_id, sizeof(r1kh->id)); ++ os_memcpy(r1kh->key, r1kh_wildcard->key, sizeof(r1kh->key)); ++ r1kh->next = *wpa_auth->conf.r1kh_list; ++ *wpa_auth->conf.r1kh_list = r1kh; ++ ++ if (timeout > 0) ++ eloop_register_timeout(timeout, 0, wpa_ft_rrb_del_r1kh, ++ wpa_auth, r1kh); ++ ++ if (wpa_ft_rrb_init_r1kh_seq(r1kh) < 0) ++ return NULL; ++ ++ return r1kh; ++} ++ ++ ++void wpa_ft_sta_deinit(struct wpa_state_machine *sm) ++{ ++ eloop_cancel_timeout(wpa_ft_expire_pull, sm, NULL); ++} ++ ++ ++static void wpa_ft_deinit_seq(struct wpa_authenticator *wpa_auth) ++{ ++ struct ft_remote_r0kh *r0kh; ++ struct ft_remote_r1kh *r1kh; ++ ++ eloop_cancel_timeout(wpa_ft_rrb_seq_timeout, wpa_auth, ELOOP_ALL_CTX); ++ ++ if (wpa_auth->conf.r0kh_list) ++ r0kh = *wpa_auth->conf.r0kh_list; ++ else ++ r0kh = NULL; ++ for (; r0kh; r0kh = r0kh->next) { ++ if (!r0kh->seq) ++ continue; ++ wpa_ft_rrb_seq_flush(wpa_auth, r0kh->seq, 0); ++ os_free(r0kh->seq); ++ r0kh->seq = NULL; ++ } ++ ++ if (wpa_auth->conf.r1kh_list) ++ r1kh = *wpa_auth->conf.r1kh_list; ++ else ++ r1kh = NULL; ++ for (; r1kh; r1kh = r1kh->next) { ++ if (!r1kh->seq) ++ continue; ++ wpa_ft_rrb_seq_flush(wpa_auth, r1kh->seq, 0); ++ os_free(r1kh->seq); ++ r1kh->seq = NULL; ++ } ++} ++ ++ ++static void wpa_ft_deinit_rkh_tmp(struct wpa_authenticator *wpa_auth) ++{ ++ struct ft_remote_r0kh *r0kh, *r0kh_next, *r0kh_prev = NULL; ++ struct ft_remote_r1kh *r1kh, *r1kh_next, *r1kh_prev = NULL; ++ ++ if (wpa_auth->conf.r0kh_list) ++ r0kh = *wpa_auth->conf.r0kh_list; ++ else ++ r0kh = NULL; ++ while (r0kh) { ++ r0kh_next = r0kh->next; ++ if (eloop_cancel_timeout(wpa_ft_rrb_del_r0kh, wpa_auth, ++ r0kh) > 0) { ++ if (r0kh_prev) ++ r0kh_prev->next = r0kh_next; ++ else ++ *wpa_auth->conf.r0kh_list = r0kh_next; ++ os_free(r0kh); ++ } else { ++ r0kh_prev = r0kh; ++ } ++ r0kh = r0kh_next; ++ } ++ ++ if (wpa_auth->conf.r1kh_list) ++ r1kh = *wpa_auth->conf.r1kh_list; ++ else ++ r1kh = NULL; ++ while (r1kh) { ++ r1kh_next = r1kh->next; ++ if (eloop_cancel_timeout(wpa_ft_rrb_del_r1kh, wpa_auth, ++ r1kh) > 0) { ++ if (r1kh_prev) ++ r1kh_prev->next = r1kh_next; ++ else ++ *wpa_auth->conf.r1kh_list = r1kh_next; ++ os_free(r1kh); ++ } else { ++ r1kh_prev = r1kh; ++ } ++ r1kh = r1kh_next; ++ } ++} ++ ++ ++void wpa_ft_deinit(struct wpa_authenticator *wpa_auth) ++{ ++ wpa_ft_deinit_seq(wpa_auth); ++ wpa_ft_deinit_rkh_tmp(wpa_auth); ++} ++ ++ ++static void wpa_ft_block_r0kh(struct wpa_authenticator *wpa_auth, ++ const u8 *f_r0kh_id, size_t f_r0kh_id_len) ++{ ++ struct ft_remote_r0kh *r0kh, *r0kh_wildcard; ++ ++ if (!wpa_auth->conf.rkh_neg_timeout) ++ return; ++ ++ wpa_ft_rrb_lookup_r0kh(wpa_auth, f_r0kh_id, f_r0kh_id_len, ++ &r0kh, &r0kh_wildcard); ++ ++ if (!r0kh_wildcard) { ++ /* r0kh removed after neg_timeout and might need re-adding */ ++ return; ++ } ++ ++ wpa_hexdump(MSG_DEBUG, "FT: Blacklist R0KH-ID", ++ f_r0kh_id, f_r0kh_id_len); ++ ++ if (r0kh) { ++ wpa_ft_rrb_r0kh_timeout(wpa_auth, r0kh, ++ wpa_auth->conf.rkh_neg_timeout); ++ os_memset(r0kh->addr, 0, ETH_ALEN); ++ } else ++ wpa_ft_rrb_add_r0kh(wpa_auth, r0kh_wildcard, NULL, f_r0kh_id, ++ f_r0kh_id_len, ++ wpa_auth->conf.rkh_neg_timeout); ++} ++ ++ ++static void wpa_ft_expire_pull(void *eloop_ctx, void *timeout_ctx) ++{ ++ struct wpa_state_machine *sm = eloop_ctx; ++ ++ wpa_printf(MSG_DEBUG, "FT: Timeout pending pull request for " MACSTR, ++ MAC2STR(sm->addr)); ++ if (sm->ft_pending_pull_left_retries <= 0) ++ wpa_ft_block_r0kh(sm->wpa_auth, sm->r0kh_id, sm->r0kh_id_len); ++ ++ /* cancel multiple timeouts */ ++ eloop_cancel_timeout(wpa_ft_expire_pull, sm, NULL); ++ ft_finish_pull(sm); ++} ++ ++ + static int wpa_ft_pull_pmk_r1(struct wpa_state_machine *sm, + const u8 *ies, size_t ies_len, + const u8 *pmk_r0_name) + { +- struct ft_remote_r0kh *r0kh; +- struct ft_r0kh_r1kh_pull_frame frame, f; ++ struct ft_remote_r0kh *r0kh, *r0kh_wildcard; ++ u8 *packet = NULL; ++ const u8 *key, *f_r1kh_id = sm->wpa_auth->conf.r1_key_holder; ++ size_t packet_len, key_len; ++ struct ft_rrb_seq f_seq; ++ int tsecs, tusecs, first; ++ struct wpabuf *ft_pending_req_ies; ++ int r0kh_timeout; ++ struct tlv_list req_enc[] = { ++ { .type = FT_RRB_PMK_R0_NAME, .len = WPA_PMK_NAME_LEN, ++ .data = pmk_r0_name }, ++ { .type = FT_RRB_S1KH_ID, .len = ETH_ALEN, ++ .data = sm->addr }, ++ { .type = FT_RRB_LAST_EMPTY, .len = 0, .data = NULL }, ++ }; ++ struct tlv_list req_auth[] = { ++ { .type = FT_RRB_NONCE, .len = FT_RRB_NONCE_LEN, ++ .data = sm->ft_pending_pull_nonce }, ++ { .type = FT_RRB_SEQ, .len = sizeof(f_seq), ++ .data = (u8 *) &f_seq }, ++ { .type = FT_RRB_R0KH_ID, .len = sm->r0kh_id_len, ++ .data = sm->r0kh_id }, ++ { .type = FT_RRB_R1KH_ID, .len = FT_R1KH_ID_LEN, ++ .data = f_r1kh_id }, ++ { .type = FT_RRB_LAST_EMPTY, .len = 0, .data = NULL }, ++ }; + +- r0kh = sm->wpa_auth->conf.r0kh_list; +- while (r0kh) { +- if (r0kh->id_len == sm->r0kh_id_len && +- os_memcmp_const(r0kh->id, sm->r0kh_id, sm->r0kh_id_len) == +- 0) +- break; +- r0kh = r0kh->next; ++ if (sm->ft_pending_pull_left_retries <= 0) ++ return -1; ++ first = sm->ft_pending_pull_left_retries == ++ sm->wpa_auth->conf.rkh_pull_retries; ++ sm->ft_pending_pull_left_retries--; ++ ++ wpa_ft_rrb_lookup_r0kh(sm->wpa_auth, sm->r0kh_id, sm->r0kh_id_len, ++ &r0kh, &r0kh_wildcard); ++ ++ /* Keep r0kh sufficiently long in the list for seq num check */ ++ r0kh_timeout = sm->wpa_auth->conf.rkh_pull_timeout / 1000 + ++ 1 + ftRRBseqTimeout; ++ if (r0kh) { ++ wpa_ft_rrb_r0kh_replenish(sm->wpa_auth, r0kh, r0kh_timeout); ++ } else if (r0kh_wildcard) { ++ wpa_printf(MSG_DEBUG, "FT: Using wildcard R0KH-ID"); ++ /* r0kh->addr: updated by SEQ_RESP and wpa_ft_expire_pull */ ++ r0kh = wpa_ft_rrb_add_r0kh(sm->wpa_auth, r0kh_wildcard, ++ r0kh_wildcard->addr, ++ sm->r0kh_id, sm->r0kh_id_len, ++ r0kh_timeout); + } + if (r0kh == NULL) { + wpa_hexdump(MSG_DEBUG, "FT: Did not find R0KH-ID", +@@ -321,51 +1982,105 @@ + sm->r0kh_id, sm->r0kh_id_len); + return -1; + } ++ if (is_zero_ether_addr(r0kh->addr)) { ++ wpa_hexdump(MSG_DEBUG, "FT: R0KH-ID is blacklisted", ++ sm->r0kh_id, sm->r0kh_id_len); ++ return -1; ++ } ++ if (os_memcmp(r0kh->addr, sm->wpa_auth->addr, ETH_ALEN) == 0) { ++ wpa_printf(MSG_DEBUG, ++ "FT: R0KH-ID points to self - no matching key available"); ++ return -1; ++ } + ++ key = r0kh->key; ++ key_len = sizeof(r0kh->key); ++ + wpa_printf(MSG_DEBUG, "FT: Send PMK-R1 pull request to remote R0KH " + "address " MACSTR, MAC2STR(r0kh->addr)); + +- os_memset(&frame, 0, sizeof(frame)); +- frame.frame_type = RSN_REMOTE_FRAME_TYPE_FT_RRB; +- frame.packet_type = FT_PACKET_R0KH_R1KH_PULL; +- frame.data_length = host_to_le16(FT_R0KH_R1KH_PULL_DATA_LEN); +- os_memcpy(frame.ap_address, sm->wpa_auth->addr, ETH_ALEN); ++ if (r0kh->seq->rx.num_last == 0) { ++ /* A sequence request will be sent out anyway when pull ++ * response is received. Send it out now to avoid one RTT. */ ++ wpa_ft_rrb_seq_req(sm->wpa_auth, r0kh->seq, r0kh->addr, ++ r0kh->id, r0kh->id_len, f_r1kh_id, key, ++ key_len, NULL, 0, NULL, 0, NULL); ++ } + +- /* aes_wrap() does not support inplace encryption, so use a temporary +- * buffer for the data. */ +- if (random_get_bytes(f.nonce, FT_R0KH_R1KH_PULL_NONCE_LEN)) { ++ if (first && ++ random_get_bytes(sm->ft_pending_pull_nonce, FT_RRB_NONCE_LEN) < 0) { + wpa_printf(MSG_DEBUG, "FT: Failed to get random data for " + "nonce"); + return -1; + } +- os_memcpy(sm->ft_pending_pull_nonce, f.nonce, +- FT_R0KH_R1KH_PULL_NONCE_LEN); +- os_memcpy(f.pmk_r0_name, pmk_r0_name, WPA_PMK_NAME_LEN); +- os_memcpy(f.r1kh_id, sm->wpa_auth->conf.r1_key_holder, FT_R1KH_ID_LEN); +- os_memcpy(f.s1kh_id, sm->addr, ETH_ALEN); +- os_memset(f.pad, 0, sizeof(f.pad)); + +- if (aes_wrap(r0kh->key, sizeof(r0kh->key), +- (FT_R0KH_R1KH_PULL_DATA_LEN + 7) / 8, +- f.nonce, frame.nonce) < 0) ++ if (wpa_ft_new_seq(r0kh->seq, &f_seq) < 0) { ++ wpa_printf(MSG_DEBUG, "FT: Failed to get seq num"); + return -1; ++ } + ++ if (wpa_ft_rrb_build(key, key_len, req_enc, NULL, req_auth, NULL, ++ sm->wpa_auth->addr, FT_PACKET_R0KH_R1KH_PULL, ++ &packet, &packet_len) < 0) ++ return -1; ++ ++ ft_pending_req_ies = wpabuf_alloc_copy(ies, ies_len); + wpabuf_free(sm->ft_pending_req_ies); +- sm->ft_pending_req_ies = wpabuf_alloc_copy(ies, ies_len); +- if (sm->ft_pending_req_ies == NULL) ++ sm->ft_pending_req_ies = ft_pending_req_ies; ++ if (!sm->ft_pending_req_ies) { ++ os_free(packet); + return -1; ++ } + +- wpa_ft_rrb_send(sm->wpa_auth, r0kh->addr, (u8 *) &frame, sizeof(frame)); ++ tsecs = sm->wpa_auth->conf.rkh_pull_timeout / 1000; ++ tusecs = (sm->wpa_auth->conf.rkh_pull_timeout % 1000) * 1000; ++ eloop_register_timeout(tsecs, tusecs, wpa_ft_expire_pull, sm, NULL); + ++ wpa_ft_rrb_oui_send(sm->wpa_auth, r0kh->addr, FT_PACKET_R0KH_R1KH_PULL, ++ packet, packet_len); ++ ++ os_free(packet); ++ + return 0; + } + + +-int wpa_auth_derive_ptk_ft(struct wpa_state_machine *sm, const u8 *pmk, +- struct wpa_ptk *ptk) ++int wpa_ft_store_pmk_fils(struct wpa_state_machine *sm, ++ const u8 *pmk_r0, const u8 *pmk_r0_name) + { +- u8 pmk_r0[PMK_LEN], pmk_r0_name[WPA_PMK_NAME_LEN]; +- u8 pmk_r1[PMK_LEN]; ++ int expires_in = sm->wpa_auth->conf.r0_key_lifetime; ++ struct vlan_description vlan; ++ const u8 *identity, *radius_cui; ++ size_t identity_len, radius_cui_len; ++ int session_timeout; ++ size_t pmk_r0_len = wpa_key_mgmt_sha384(sm->wpa_key_mgmt) ? ++ SHA384_MAC_LEN : PMK_LEN; ++ ++ if (wpa_ft_get_vlan(sm->wpa_auth, sm->addr, &vlan) < 0) { ++ wpa_printf(MSG_DEBUG, "FT: vlan not available for STA " MACSTR, ++ MAC2STR(sm->addr)); ++ return -1; ++ } ++ ++ identity_len = wpa_ft_get_identity(sm->wpa_auth, sm->addr, &identity); ++ radius_cui_len = wpa_ft_get_radius_cui(sm->wpa_auth, sm->addr, ++ &radius_cui); ++ session_timeout = wpa_ft_get_session_timeout(sm->wpa_auth, sm->addr); ++ ++ return wpa_ft_store_pmk_r0(sm->wpa_auth, sm->addr, pmk_r0, pmk_r0_len, ++ pmk_r0_name, sm->pairwise, &vlan, expires_in, ++ session_timeout, identity, identity_len, ++ radius_cui, radius_cui_len); ++} ++ ++ ++int wpa_auth_derive_ptk_ft(struct wpa_state_machine *sm, struct wpa_ptk *ptk) ++{ ++ u8 pmk_r0[PMK_LEN_MAX], pmk_r0_name[WPA_PMK_NAME_LEN]; ++ size_t pmk_r0_len = wpa_key_mgmt_sha384(sm->wpa_key_mgmt) ? ++ SHA384_MAC_LEN : PMK_LEN; ++ size_t pmk_r1_len = pmk_r0_len; ++ u8 pmk_r1[PMK_LEN_MAX]; + 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; +@@ -373,6 +2088,12 @@ + 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; ++ int psk_local = sm->wpa_auth->conf.ft_psk_generate_local; ++ int expires_in = sm->wpa_auth->conf.r0_key_lifetime; ++ struct vlan_description vlan; ++ const u8 *identity, *radius_cui; ++ size_t identity_len, radius_cui_len; ++ int session_timeout; + + if (sm->xxkey_len == 0) { + wpa_printf(MSG_DEBUG, "FT: XXKey not available for key " +@@ -380,23 +2101,45 @@ + 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); ++ if (wpa_ft_get_vlan(sm->wpa_auth, sm->addr, &vlan) < 0) { ++ wpa_printf(MSG_DEBUG, "FT: vlan not available for STA " MACSTR, ++ MAC2STR(sm->addr)); ++ return -1; ++ } ++ ++ identity_len = wpa_ft_get_identity(sm->wpa_auth, sm->addr, &identity); ++ radius_cui_len = wpa_ft_get_radius_cui(sm->wpa_auth, sm->addr, ++ &radius_cui); ++ session_timeout = wpa_ft_get_session_timeout(sm->wpa_auth, sm->addr); ++ ++ if (wpa_derive_pmk_r0(sm->xxkey, sm->xxkey_len, ssid, ssid_len, mdid, ++ r0kh, r0kh_len, sm->addr, ++ pmk_r0, pmk_r0_name, ++ wpa_key_mgmt_sha384(sm->wpa_key_mgmt)) < 0) ++ return -1; ++ wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R0", pmk_r0, pmk_r0_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); ++ if (!psk_local || !wpa_key_mgmt_ft_psk(sm->wpa_key_mgmt)) ++ wpa_ft_store_pmk_r0(sm->wpa_auth, sm->addr, pmk_r0, pmk_r0_len, ++ pmk_r0_name, ++ sm->pairwise, &vlan, expires_in, ++ session_timeout, identity, identity_len, ++ radius_cui, radius_cui_len); + +- 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); ++ if (wpa_derive_pmk_r1(pmk_r0, pmk_r0_len, pmk_r0_name, r1kh, sm->addr, ++ pmk_r1, sm->pmk_r1_name) < 0) ++ return -1; ++ wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1", pmk_r1, pmk_r1_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); ++ if (!psk_local || !wpa_key_mgmt_ft_psk(sm->wpa_key_mgmt)) ++ wpa_ft_store_pmk_r1(sm->wpa_auth, sm->addr, pmk_r1, pmk_r1_len, ++ sm->pmk_r1_name, sm->pairwise, &vlan, ++ expires_in, session_timeout, identity, ++ identity_len, radius_cui, radius_cui_len); + +- return wpa_pmk_r1_to_ptk(pmk_r1, sm->SNonce, sm->ANonce, sm->addr, +- sm->wpa_auth->addr, sm->pmk_r1_name, ++ return wpa_pmk_r1_to_ptk(pmk_r1, pmk_r1_len, sm->SNonce, sm->ANonce, ++ sm->addr, sm->wpa_auth->addr, sm->pmk_r1_name, + ptk, ptk_name, sm->wpa_key_mgmt, sm->pairwise); + } + +@@ -404,9 +2147,9 @@ + 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) ++ if (wpa_auth->cb->get_seqnum == NULL) + return -1; +- return wpa_auth->cb.get_seqnum(wpa_auth->cb.ctx, addr, idx, seq); ++ return wpa_auth->cb->get_seqnum(wpa_auth->cb_ctx, addr, idx, seq); + } + + +@@ -418,7 +2161,17 @@ + const u8 *key; + size_t key_len; + u8 keybuf[32]; ++ const u8 *kek; ++ size_t kek_len; + ++ if (wpa_key_mgmt_fils(sm->wpa_key_mgmt)) { ++ kek = sm->PTK.kek2; ++ kek_len = sm->PTK.kek2_len; ++ } else { ++ kek = sm->PTK.kek; ++ kek_len = sm->PTK.kek_len; ++ } ++ + key_len = gsm->GTK_len; + if (key_len > sizeof(keybuf)) + return NULL; +@@ -456,8 +2209,10 @@ + WPA_PUT_LE16(&subelem[2], gsm->GN & 0x03); + subelem[4] = gsm->GTK_len; + wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN, subelem + 5); +- if (aes_wrap(sm->PTK.kek, sm->PTK.kek_len, key_len / 8, key, +- subelem + 13)) { ++ if (aes_wrap(kek, kek_len, key_len / 8, key, subelem + 13)) { ++ wpa_printf(MSG_DEBUG, ++ "FT: GTK subelem encryption failed: kek_len=%d", ++ (int) kek_len); + os_free(subelem); + return NULL; + } +@@ -473,10 +2228,23 @@ + u8 *subelem, *pos; + struct wpa_group *gsm = sm->group; + size_t subelem_len; ++ const u8 *kek; ++ size_t kek_len; ++ size_t igtk_len; + ++ if (wpa_key_mgmt_fils(sm->wpa_key_mgmt)) { ++ kek = sm->PTK.kek2; ++ kek_len = sm->PTK.kek2_len; ++ } else { ++ kek = sm->PTK.kek; ++ kek_len = sm->PTK.kek_len; ++ } ++ ++ igtk_len = wpa_cipher_key_len(sm->wpa_auth->conf.group_mgmt_cipher); ++ + /* 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_len = 1 + 1 + 2 + 6 + 1 + igtk_len + 8; + subelem = os_zalloc(subelem_len); + if (subelem == NULL) + return NULL; +@@ -488,9 +2256,12 @@ + pos += 2; + wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN_igtk, pos); + pos += 6; +- *pos++ = WPA_IGTK_LEN; +- if (aes_wrap(sm->PTK.kek, sm->PTK.kek_len, WPA_IGTK_LEN / 8, ++ *pos++ = igtk_len; ++ if (aes_wrap(kek, kek_len, igtk_len / 8, + gsm->IGTK[gsm->GN_igtk - 4], pos)) { ++ wpa_printf(MSG_DEBUG, ++ "FT: IGTK subelem encryption failed: kek_len=%d", ++ (int) kek_len); + os_free(subelem); + return NULL; + } +@@ -637,17 +2408,21 @@ + const u8 *req_ies, size_t req_ies_len) + { + u8 *end, *mdie, *ftie, *rsnie = NULL, *r0kh_id, *subelem = NULL; ++ u8 *fte_mic, *elem_count; + 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; ++ const u8 *kck; ++ size_t kck_len; ++ int use_sha384; + + if (sm == NULL) + return pos; + ++ use_sha384 = wpa_key_mgmt_sha384(sm->wpa_key_mgmt); + conf = &sm->wpa_auth->conf; + + if (!wpa_key_mgmt_ft(sm->wpa_key_mgmt)) +@@ -655,14 +2430,28 @@ + + end = pos + max_len; + +- if (auth_alg == WLAN_AUTH_FT) { ++ if (auth_alg == WLAN_AUTH_FT || ++ ((auth_alg == WLAN_AUTH_FILS_SK || ++ auth_alg == WLAN_AUTH_FILS_SK_PFS || ++ auth_alg == WLAN_AUTH_FILS_PK) && ++ (sm->wpa_key_mgmt & (WPA_KEY_MGMT_FT_FILS_SHA256 | ++ WPA_KEY_MGMT_FT_FILS_SHA384)))) { ++ if (!sm->pmk_r1_name_valid) { ++ wpa_printf(MSG_ERROR, ++ "FT: PMKR1Name is not valid for Assoc Resp RSNE"); ++ return NULL; ++ } ++ wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name for Assoc Resp RSNE", ++ sm->pmk_r1_name, WPA_PMK_NAME_LEN); + /* + * RSN (only present if this is a Reassociation Response and +- * part of a fast BSS transition) ++ * part of a fast BSS transition; or if this is a ++ * (Re)Association Response frame during an FT initial mobility ++ * domain association using FILS) + */ + res = wpa_write_rsn_ie(conf, pos, end - pos, sm->pmk_r1_name); + if (res < 0) +- return pos; ++ return NULL; + rsnie = pos; + rsnie_len = res; + pos += res; +@@ -671,7 +2460,7 @@ + /* Mobility Domain Information */ + res = wpa_write_mdie(conf, pos, end - pos); + if (res < 0) +- return pos; ++ return NULL; + mdie = pos; + mdie_len = res; + pos += res; +@@ -679,6 +2468,11 @@ + /* Fast BSS Transition Information */ + if (auth_alg == WLAN_AUTH_FT) { + subelem = wpa_ft_gtk_subelem(sm, &subelem_len); ++ if (!subelem) { ++ wpa_printf(MSG_DEBUG, ++ "FT: Failed to add GTK subelement"); ++ return NULL; ++ } + r0kh_id = sm->r0kh_id; + r0kh_id_len = sm->r0kh_id_len; + anonce = sm->ANonce; +@@ -690,14 +2484,16 @@ + u8 *nbuf; + igtk = wpa_ft_igtk_subelem(sm, &igtk_len); + if (igtk == NULL) { ++ wpa_printf(MSG_DEBUG, ++ "FT: Failed to add IGTK subelement"); + os_free(subelem); +- return pos; ++ return NULL; + } + nbuf = os_realloc(subelem, subelem_len + igtk_len); + if (nbuf == NULL) { + os_free(subelem); + os_free(igtk); +- return pos; ++ return NULL; + } + subelem = nbuf; + os_memcpy(subelem + subelem_len, igtk, igtk_len); +@@ -705,6 +2501,35 @@ + os_free(igtk); + } + #endif /* CONFIG_IEEE80211W */ ++#ifdef CONFIG_OCV ++ if (wpa_auth_uses_ocv(sm)) { ++ struct wpa_channel_info ci; ++ u8 *nbuf, *ocipos; ++ ++ if (wpa_channel_info(sm->wpa_auth, &ci) != 0) { ++ wpa_printf(MSG_WARNING, ++ "Failed to get channel info for OCI element"); ++ os_free(subelem); ++ return NULL; ++ } ++ ++ subelem_len += 2 + OCV_OCI_LEN; ++ nbuf = os_realloc(subelem, subelem_len); ++ if (!nbuf) { ++ os_free(subelem); ++ return NULL; ++ } ++ subelem = nbuf; ++ ++ ocipos = subelem + subelem_len - 2 - OCV_OCI_LEN; ++ *ocipos++ = FTIE_SUBELEM_OCI; ++ *ocipos++ = OCV_OCI_LEN; ++ if (ocv_insert_oci(&ci, &ocipos) < 0) { ++ os_free(subelem); ++ return NULL; ++ } ++ } ++#endif /* CONFIG_OCV */ + } else { + r0kh_id = conf->r0_key_holder; + r0kh_id_len = conf->r0_key_holder_len; +@@ -711,30 +2536,38 @@ + anonce = NULL; + snonce = NULL; + } +- res = wpa_write_ftie(conf, r0kh_id, r0kh_id_len, anonce, snonce, pos, +- end - pos, subelem, subelem_len); ++ res = wpa_write_ftie(conf, use_sha384, r0kh_id, r0kh_id_len, ++ anonce, snonce, pos, end - pos, ++ subelem, subelem_len); + os_free(subelem); + if (res < 0) +- return pos; ++ return NULL; + 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); ++ if (use_sha384) { ++ struct rsn_ftie_sha384 *_ftie = ++ (struct rsn_ftie_sha384 *) (ftie + 2); + +- _ftie = (struct rsn_ftie *) (ftie + 2); ++ fte_mic = _ftie->mic; ++ elem_count = &_ftie->mic_control[1]; ++ } else { ++ struct rsn_ftie *_ftie = (struct rsn_ftie *) (ftie + 2); ++ ++ fte_mic = _ftie->mic; ++ elem_count = &_ftie->mic_control[1]; ++ } + if (auth_alg == WLAN_AUTH_FT) +- _ftie->mic_control[1] = 3; /* Information element count */ ++ *elem_count = 3; /* Information element count */ + + ric_start = pos; +- if (wpa_ft_parse_ies(req_ies, req_ies_len, &parse) == 0 && parse.ric) { ++ if (wpa_ft_parse_ies(req_ies, req_ies_len, &parse, use_sha384) == 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] += ++ *elem_count += + ieee802_11_ie_count(ric_start, + pos - ric_start); + } +@@ -741,15 +2574,29 @@ + if (ric_start == pos) + ric_start = NULL; + ++ if (wpa_key_mgmt_fils(sm->wpa_key_mgmt)) { ++ kck = sm->PTK.kck2; ++ kck_len = sm->PTK.kck2_len; ++ } else { ++ kck = sm->PTK.kck; ++ kck_len = sm->PTK.kck_len; ++ } + if (auth_alg == WLAN_AUTH_FT && +- wpa_ft_mic(sm->PTK.kck, sm->PTK.kck_len, sm->addr, +- sm->wpa_auth->addr, 6, ++ wpa_ft_mic(kck, kck_len, sm->addr, sm->wpa_auth->addr, 6, + mdie, mdie_len, ftie, ftie_len, + rsnie, rsnie_len, + ric_start, ric_start ? pos - ric_start : 0, +- _ftie->mic) < 0) ++ fte_mic) < 0) { + wpa_printf(MSG_DEBUG, "FT: Failed to calculate MIC"); ++ return NULL; ++ } + ++ os_free(sm->assoc_resp_ftie); ++ sm->assoc_resp_ftie = os_malloc(ftie_len); ++ if (!sm->assoc_resp_ftie) ++ return NULL; ++ os_memcpy(sm->assoc_resp_ftie, ftie, ftie_len); ++ + return pos; + } + +@@ -759,10 +2606,10 @@ + enum wpa_alg alg, const u8 *addr, int idx, + u8 *key, size_t key_len) + { +- if (wpa_auth->cb.set_key == NULL) ++ 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); ++ return wpa_auth->cb->set_key(wpa_auth->cb_ctx, vlan_id, alg, addr, idx, ++ key, key_len); + } + + +@@ -804,13 +2651,209 @@ + } + + ++/* Derive PMK-R1 from PSK, check all available PSK */ ++static int wpa_ft_psk_pmk_r1(struct wpa_state_machine *sm, ++ const u8 *req_pmk_r1_name, ++ u8 *out_pmk_r1, int *out_pairwise, ++ struct vlan_description *out_vlan, ++ const u8 **out_identity, size_t *out_identity_len, ++ const u8 **out_radius_cui, ++ size_t *out_radius_cui_len, ++ int *out_session_timeout) ++{ ++ const u8 *pmk = NULL; ++ u8 pmk_r0[PMK_LEN], pmk_r0_name[WPA_PMK_NAME_LEN]; ++ u8 pmk_r1[PMK_LEN], pmk_r1_name[WPA_PMK_NAME_LEN]; ++ struct wpa_authenticator *wpa_auth = sm->wpa_auth; ++ const u8 *mdid = wpa_auth->conf.mobility_domain; ++ const u8 *r0kh = sm->r0kh_id; ++ size_t r0kh_len = sm->r0kh_id_len; ++ const u8 *r1kh = wpa_auth->conf.r1_key_holder; ++ const u8 *ssid = wpa_auth->conf.ssid; ++ size_t ssid_len = wpa_auth->conf.ssid_len; ++ int pairwise; ++ ++ pairwise = sm->pairwise; ++ ++ for (;;) { ++ pmk = wpa_ft_get_psk(wpa_auth, sm->addr, sm->p2p_dev_addr, ++ pmk); ++ if (pmk == NULL) ++ break; ++ ++ if (wpa_derive_pmk_r0(pmk, PMK_LEN, ssid, ssid_len, mdid, r0kh, ++ r0kh_len, sm->addr, ++ pmk_r0, pmk_r0_name, 0) < 0 || ++ wpa_derive_pmk_r1(pmk_r0, PMK_LEN, pmk_r0_name, r1kh, ++ sm->addr, pmk_r1, pmk_r1_name) < 0 || ++ os_memcmp_const(pmk_r1_name, req_pmk_r1_name, ++ WPA_PMK_NAME_LEN) != 0) ++ continue; ++ ++ /* We found a PSK that matches the requested pmk_r1_name */ ++ wpa_printf(MSG_DEBUG, ++ "FT: Found PSK to generate PMK-R1 locally"); ++ os_memcpy(out_pmk_r1, pmk_r1, PMK_LEN); ++ if (out_pairwise) ++ *out_pairwise = pairwise; ++ os_memcpy(sm->PMK, pmk, PMK_LEN); ++ sm->pmk_len = PMK_LEN; ++ if (out_vlan && ++ wpa_ft_get_vlan(sm->wpa_auth, sm->addr, out_vlan) < 0) { ++ wpa_printf(MSG_DEBUG, "FT: vlan not available for STA " ++ MACSTR, MAC2STR(sm->addr)); ++ return -1; ++ } ++ ++ if (out_identity && out_identity_len) { ++ *out_identity_len = wpa_ft_get_identity( ++ sm->wpa_auth, sm->addr, out_identity); ++ } ++ ++ if (out_radius_cui && out_radius_cui_len) { ++ *out_radius_cui_len = wpa_ft_get_radius_cui( ++ sm->wpa_auth, sm->addr, out_radius_cui); ++ } ++ ++ if (out_session_timeout) { ++ *out_session_timeout = wpa_ft_get_session_timeout( ++ sm->wpa_auth, sm->addr); ++ } ++ ++ return 0; ++ } ++ ++ wpa_printf(MSG_DEBUG, ++ "FT: Did not find PSK to generate PMK-R1 locally"); ++ return -1; ++} ++ ++ ++/* Detect the configuration the station asked for. ++ * Required to detect FT-PSK and pairwise cipher. ++ */ ++static int wpa_ft_set_key_mgmt(struct wpa_state_machine *sm, ++ struct wpa_ft_ies *parse) ++{ ++ int key_mgmt, ciphers; ++ ++ if (sm->wpa_key_mgmt) ++ return 0; ++ ++ key_mgmt = parse->key_mgmt & sm->wpa_auth->conf.wpa_key_mgmt; ++ if (!key_mgmt) { ++ wpa_printf(MSG_DEBUG, "FT: Invalid key mgmt (0x%x) from " ++ MACSTR, parse->key_mgmt, MAC2STR(sm->addr)); ++ return -1; ++ } ++ if (key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X) ++ sm->wpa_key_mgmt = WPA_KEY_MGMT_FT_IEEE8021X; ++#ifdef CONFIG_SHA384 ++ else if (key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X_SHA384) ++ sm->wpa_key_mgmt = WPA_KEY_MGMT_FT_IEEE8021X_SHA384; ++#endif /* CONFIG_SHA384 */ ++ else if (key_mgmt & WPA_KEY_MGMT_FT_PSK) ++ sm->wpa_key_mgmt = WPA_KEY_MGMT_FT_PSK; ++#ifdef CONFIG_FILS ++ else if (key_mgmt & WPA_KEY_MGMT_FT_FILS_SHA256) ++ sm->wpa_key_mgmt = WPA_KEY_MGMT_FT_FILS_SHA256; ++ else if (key_mgmt & WPA_KEY_MGMT_FT_FILS_SHA384) ++ sm->wpa_key_mgmt = WPA_KEY_MGMT_FT_FILS_SHA384; ++#endif /* CONFIG_FILS */ ++ ciphers = parse->pairwise_cipher & sm->wpa_auth->conf.rsn_pairwise; ++ if (!ciphers) { ++ wpa_printf(MSG_DEBUG, "FT: Invalid pairwise cipher (0x%x) from " ++ MACSTR, ++ parse->pairwise_cipher, MAC2STR(sm->addr)); ++ return -1; ++ } ++ sm->pairwise = wpa_pick_pairwise_cipher(ciphers, 0); ++ ++ return 0; ++} ++ ++ ++static int wpa_ft_local_derive_pmk_r1(struct wpa_authenticator *wpa_auth, ++ struct wpa_state_machine *sm, ++ const u8 *r0kh_id, size_t r0kh_id_len, ++ const u8 *req_pmk_r0_name, ++ const u8 *req_pmk_r1_name, ++ u8 *out_pmk_r1, int *out_pairwise, ++ struct vlan_description *vlan, ++ const u8 **identity, size_t *identity_len, ++ const u8 **radius_cui, ++ size_t *radius_cui_len, ++ int *out_session_timeout) ++{ ++ struct wpa_auth_config *conf = &wpa_auth->conf; ++ const struct wpa_ft_pmk_r0_sa *r0; ++ u8 pmk_r1_name[WPA_PMK_NAME_LEN]; ++ int expires_in = 0; ++ int session_timeout = 0; ++ struct os_reltime now; ++ ++ if (conf->r0_key_holder_len != r0kh_id_len || ++ os_memcmp(conf->r0_key_holder, r0kh_id, conf->r0_key_holder_len) != ++ 0) ++ return -1; /* not our R0KH-ID */ ++ ++ wpa_printf(MSG_DEBUG, "FT: STA R0KH-ID matching local configuration"); ++ if (wpa_ft_fetch_pmk_r0(sm->wpa_auth, sm->addr, req_pmk_r0_name, &r0) < ++ 0) ++ return -1; /* no matching PMKR0Name in local cache */ ++ ++ wpa_printf(MSG_DEBUG, "FT: Requested PMKR0Name found in local cache"); ++ ++ if (wpa_derive_pmk_r1(r0->pmk_r0, r0->pmk_r0_len, r0->pmk_r0_name, ++ conf->r1_key_holder, ++ sm->addr, out_pmk_r1, pmk_r1_name) < 0) ++ return -1; ++ wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1", out_pmk_r1, r0->pmk_r0_len); ++ wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name", pmk_r1_name, WPA_PMK_NAME_LEN); ++ ++ os_get_reltime(&now); ++ if (r0->expiration) ++ expires_in = r0->expiration - now.sec; ++ ++ if (r0->session_timeout) ++ session_timeout = r0->session_timeout - now.sec; ++ ++ wpa_ft_store_pmk_r1(wpa_auth, sm->addr, out_pmk_r1, r0->pmk_r0_len, ++ pmk_r1_name, ++ sm->pairwise, r0->vlan, expires_in, session_timeout, ++ r0->identity, r0->identity_len, ++ r0->radius_cui, r0->radius_cui_len); ++ ++ *out_pairwise = sm->pairwise; ++ if (vlan) { ++ if (r0->vlan) ++ *vlan = *r0->vlan; ++ else ++ os_memset(vlan, 0, sizeof(*vlan)); ++ } ++ ++ if (identity && identity_len) { ++ *identity = r0->identity; ++ *identity_len = r0->identity_len; ++ } ++ ++ if (radius_cui && radius_cui_len) { ++ *radius_cui = r0->radius_cui; ++ *radius_cui_len = r0->radius_cui_len; ++ } ++ ++ *out_session_timeout = session_timeout; ++ ++ return 0; ++} ++ ++ + static int wpa_ft_process_auth_req(struct wpa_state_machine *sm, + const u8 *ies, size_t ies_len, + u8 **resp_ies, size_t *resp_ies_len) + { + struct rsn_mdie *mdie; +- struct rsn_ftie *ftie; +- u8 pmk_r1[PMK_LEN], pmk_r1_name[WPA_PMK_NAME_LEN]; ++ u8 pmk_r1[PMK_LEN_MAX], pmk_r1_name[WPA_PMK_NAME_LEN]; + u8 ptk_name[WPA_PMK_NAME_LEN]; + struct wpa_auth_config *conf; + struct wpa_ft_ies parse; +@@ -817,7 +2860,12 @@ + size_t buflen; + int ret; + u8 *pos, *end; +- int pairwise; ++ int pairwise, session_timeout = 0; ++ struct vlan_description vlan; ++ const u8 *identity, *radius_cui; ++ size_t identity_len = 0, radius_cui_len = 0; ++ int use_sha384; ++ size_t pmk_r1_len; + + *resp_ies = NULL; + *resp_ies_len = 0; +@@ -828,10 +2876,12 @@ + wpa_hexdump(MSG_DEBUG, "FT: Received authentication frame IEs", + ies, ies_len); + +- if (wpa_ft_parse_ies(ies, ies_len, &parse) < 0) { ++ if (wpa_ft_parse_ies(ies, ies_len, &parse, -1)) { + wpa_printf(MSG_DEBUG, "FT: Failed to parse FT IEs"); + return WLAN_STATUS_UNSPECIFIED_FAILURE; + } ++ use_sha384 = wpa_key_mgmt_sha384(parse.key_mgmt); ++ pmk_r1_len = use_sha384 ? SHA384_MAC_LEN : PMK_LEN; + + mdie = (struct rsn_mdie *) parse.mdie; + if (mdie == NULL || parse.mdie_len < sizeof(*mdie) || +@@ -842,14 +2892,28 @@ + 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 (use_sha384) { ++ struct rsn_ftie_sha384 *ftie; ++ ++ ftie = (struct rsn_ftie_sha384 *) parse.ftie; ++ if (!ftie || 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); ++ } else { ++ struct rsn_ftie *ftie; ++ ++ ftie = (struct rsn_ftie *) parse.ftie; ++ if (!ftie || 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); + } + +- 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; +@@ -865,28 +2929,62 @@ + return WLAN_STATUS_INVALID_PMKID; + } + ++ if (wpa_ft_set_key_mgmt(sm, &parse) < 0) ++ return WLAN_STATUS_UNSPECIFIED_FAILURE; ++ + 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); ++ if (wpa_derive_pmk_r1_name(parse.rsn_pmkid, ++ sm->wpa_auth->conf.r1_key_holder, sm->addr, ++ pmk_r1_name, use_sha384) < 0) ++ return WLAN_STATUS_UNSPECIFIED_FAILURE; + 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 (conf->ft_psk_generate_local && ++ wpa_key_mgmt_ft_psk(sm->wpa_key_mgmt)) { ++ if (wpa_ft_psk_pmk_r1(sm, pmk_r1_name, pmk_r1, &pairwise, ++ &vlan, &identity, &identity_len, ++ &radius_cui, &radius_cui_len, ++ &session_timeout) < 0) ++ return WLAN_STATUS_INVALID_PMKID; ++ wpa_printf(MSG_DEBUG, ++ "FT: Generated PMK-R1 for FT-PSK locally"); ++ } else if (wpa_ft_fetch_pmk_r1(sm->wpa_auth, sm->addr, pmk_r1_name, ++ pmk_r1, &pmk_r1_len, &pairwise, &vlan, ++ &identity, &identity_len, &radius_cui, ++ &radius_cui_len, &session_timeout) < 0) { ++ wpa_printf(MSG_DEBUG, ++ "FT: No PMK-R1 available in local cache for the requested PMKR1Name"); ++ if (wpa_ft_local_derive_pmk_r1(sm->wpa_auth, sm, ++ parse.r0kh_id, parse.r0kh_id_len, ++ parse.rsn_pmkid, ++ pmk_r1_name, pmk_r1, &pairwise, ++ &vlan, &identity, &identity_len, ++ &radius_cui, &radius_cui_len, ++ &session_timeout) == 0) { ++ wpa_printf(MSG_DEBUG, ++ "FT: Generated PMK-R1 based on local PMK-R0"); ++ goto pmk_r1_derived; ++ } ++ + if (wpa_ft_pull_pmk_r1(sm, ies, ies_len, parse.rsn_pmkid) < 0) { +- wpa_printf(MSG_DEBUG, "FT: Did not have matching " +- "PMK-R1 and unknown R0KH-ID"); ++ wpa_printf(MSG_DEBUG, ++ "FT: Did not have matching PMK-R1 and either unknown or blocked R0KH-ID or NAK from R0KH"); + return WLAN_STATUS_INVALID_PMKID; + } + + return -1; /* Status pending */ ++ } else { ++ wpa_printf(MSG_DEBUG, "FT: Found PMKR1Name from local cache"); + } + +- wpa_hexdump_key(MSG_DEBUG, "FT: Selected PMK-R1", pmk_r1, PMK_LEN); ++pmk_r1_derived: ++ wpa_hexdump_key(MSG_DEBUG, "FT: Selected PMK-R1", pmk_r1, pmk_r1_len); + sm->pmk_r1_name_valid = 1; + os_memcpy(sm->pmk_r1_name, pmk_r1_name, WPA_PMK_NAME_LEN); ++ os_memcpy(sm->pmk_r1, pmk_r1, pmk_r1_len); ++ sm->pmk_r1_len = pmk_r1_len; + + if (random_get_bytes(sm->ANonce, WPA_NONCE_LEN)) { + wpa_printf(MSG_DEBUG, "FT: Failed to get random data for " +@@ -899,8 +2997,8 @@ + wpa_hexdump(MSG_DEBUG, "FT: Generated ANonce", + sm->ANonce, WPA_NONCE_LEN); + +- if (wpa_pmk_r1_to_ptk(pmk_r1, sm->SNonce, sm->ANonce, sm->addr, +- sm->wpa_auth->addr, pmk_r1_name, ++ if (wpa_pmk_r1_to_ptk(pmk_r1, pmk_r1_len, sm->SNonce, sm->ANonce, ++ sm->addr, sm->wpa_auth->addr, pmk_r1_name, + &sm->PTK, ptk_name, sm->wpa_key_mgmt, + pairwise) < 0) + return WLAN_STATUS_UNSPECIFIED_FAILURE; +@@ -910,44 +3008,51 @@ + sm->tk_already_set = FALSE; + wpa_ft_install_ptk(sm); + ++ if (wpa_ft_set_vlan(sm->wpa_auth, sm->addr, &vlan) < 0) { ++ wpa_printf(MSG_DEBUG, "FT: Failed to configure VLAN"); ++ return WLAN_STATUS_UNSPECIFIED_FAILURE; ++ } ++ if (wpa_ft_set_identity(sm->wpa_auth, sm->addr, ++ identity, identity_len) < 0 || ++ wpa_ft_set_radius_cui(sm->wpa_auth, sm->addr, ++ radius_cui, radius_cui_len) < 0) { ++ wpa_printf(MSG_DEBUG, "FT: Failed to configure identity/CUI"); ++ return WLAN_STATUS_UNSPECIFIED_FAILURE; ++ } ++ wpa_ft_set_session_timeout(sm->wpa_auth, sm->addr, session_timeout); ++ + 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; +- } ++ if (*resp_ies == NULL) ++ goto fail; + + 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; +- } ++ if (ret < 0) ++ goto fail; + 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; +- } ++ if (ret < 0) ++ goto fail; + pos += ret; + +- ret = wpa_write_ftie(conf, parse.r0kh_id, parse.r0kh_id_len, ++ ret = wpa_write_ftie(conf, use_sha384, 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; +- } ++ if (ret < 0) ++ goto fail; + pos += ret; + + *resp_ies_len = pos - *resp_ies; + + return WLAN_STATUS_SUCCESS; ++fail: ++ os_free(*resp_ies); ++ *resp_ies = NULL; ++ return WLAN_STATUS_UNSPECIFIED_FAILURE; + } + + +@@ -975,6 +3080,7 @@ + sm->ft_pending_cb = cb; + sm->ft_pending_cb_ctx = ctx; + sm->ft_pending_auth_transaction = auth_transaction; ++ sm->ft_pending_pull_left_retries = sm->wpa_auth->conf.rkh_pull_retries; + res = wpa_ft_process_auth_req(sm, ies, ies_len, &resp_ies, + &resp_ies_len); + if (res < 0) { +@@ -998,17 +3104,23 @@ + { + struct wpa_ft_ies parse; + struct rsn_mdie *mdie; +- struct rsn_ftie *ftie; + u8 mic[WPA_EAPOL_KEY_MIC_MAX_LEN]; + size_t mic_len = 16; + unsigned int count; ++ const u8 *kck; ++ size_t kck_len; ++ int use_sha384; ++ const u8 *anonce, *snonce, *fte_mic; ++ u8 fte_elem_count; + + if (sm == NULL) + return WLAN_STATUS_UNSPECIFIED_FAILURE; + ++ use_sha384 = wpa_key_mgmt_sha384(sm->wpa_key_mgmt); ++ + wpa_hexdump(MSG_DEBUG, "FT: Reassoc Req IEs", ies, ies_len); + +- if (wpa_ft_parse_ies(ies, ies_len, &parse) < 0) { ++ if (wpa_ft_parse_ies(ies, ies_len, &parse, use_sha384) < 0) { + wpa_printf(MSG_DEBUG, "FT: Failed to parse FT IEs"); + return WLAN_STATUS_UNSPECIFIED_FAILURE; + } +@@ -1039,34 +3151,56 @@ + 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 (use_sha384) { ++ struct rsn_ftie_sha384 *ftie; ++ ++ ftie = (struct rsn_ftie_sha384 *) parse.ftie; ++ if (ftie == NULL || parse.ftie_len < sizeof(*ftie)) { ++ wpa_printf(MSG_DEBUG, "FT: Invalid FTIE"); ++ return WLAN_STATUS_INVALID_FTIE; ++ } ++ ++ anonce = ftie->anonce; ++ snonce = ftie->snonce; ++ fte_elem_count = ftie->mic_control[1]; ++ fte_mic = ftie->mic; ++ } else { ++ struct rsn_ftie *ftie; ++ ++ 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; ++ } ++ ++ anonce = ftie->anonce; ++ snonce = ftie->snonce; ++ fte_elem_count = ftie->mic_control[1]; ++ fte_mic = ftie->mic; + } + +- if (os_memcmp(ftie->snonce, sm->SNonce, WPA_NONCE_LEN) != 0) { ++ if (os_memcmp(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); ++ snonce, WPA_NONCE_LEN); + wpa_hexdump(MSG_DEBUG, "FT: Expected SNonce", + sm->SNonce, WPA_NONCE_LEN); +- return -1; ++ return WLAN_STATUS_INVALID_FTIE; + } + +- if (os_memcmp(ftie->anonce, sm->ANonce, WPA_NONCE_LEN) != 0) { ++ if (os_memcmp(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); ++ anonce, WPA_NONCE_LEN); + wpa_hexdump(MSG_DEBUG, "FT: Expected ANonce", + sm->ANonce, WPA_NONCE_LEN); +- return -1; ++ return WLAN_STATUS_INVALID_FTIE; + } + + + if (parse.r0kh_id == NULL) { + wpa_printf(MSG_DEBUG, "FT: No R0KH-ID subelem in FTIE"); +- return -1; ++ return WLAN_STATUS_INVALID_FTIE; + } + + if (parse.r0kh_id_len != sm->r0kh_id_len || +@@ -1078,12 +3212,12 @@ + 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; ++ return WLAN_STATUS_INVALID_FTIE; + } + + if (parse.r1kh_id == NULL) { + wpa_printf(MSG_DEBUG, "FT: No R1KH-ID subelem in FTIE"); +- return -1; ++ return WLAN_STATUS_INVALID_FTIE; + } + + if (os_memcmp_const(parse.r1kh_id, sm->wpa_auth->conf.r1_key_holder, +@@ -1094,7 +3228,7 @@ + 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; ++ return WLAN_STATUS_INVALID_FTIE; + } + + if (parse.rsn_pmkid == NULL || +@@ -1102,21 +3236,27 @@ + { + wpa_printf(MSG_DEBUG, "FT: No matching PMKR1Name (PMKID) in " + "RSNIE (pmkid=%d)", !!parse.rsn_pmkid); +- return -1; ++ return WLAN_STATUS_INVALID_PMKID; + } + + count = 3; + if (parse.ric) + count += ieee802_11_ie_count(parse.ric, parse.ric_len); +- if (ftie->mic_control[1] != count) { ++ if (fte_elem_count != count) { + wpa_printf(MSG_DEBUG, "FT: Unexpected IE count in MIC " + "Control: received %u expected %u", +- ftie->mic_control[1], count); +- return -1; ++ fte_elem_count, count); ++ return WLAN_STATUS_UNSPECIFIED_FAILURE; + } + +- if (wpa_ft_mic(sm->PTK.kck, sm->PTK.kck_len, sm->addr, +- sm->wpa_auth->addr, 5, ++ if (wpa_key_mgmt_fils(sm->wpa_key_mgmt)) { ++ kck = sm->PTK.kck2; ++ kck_len = sm->PTK.kck2_len; ++ } else { ++ kck = sm->PTK.kck; ++ kck_len = sm->PTK.kck_len; ++ } ++ if (wpa_ft_mic(kck, kck_len, sm->addr, sm->wpa_auth->addr, 5, + parse.mdie - 2, parse.mdie_len + 2, + parse.ftie - 2, parse.ftie_len + 2, + parse.rsn - 2, parse.rsn_len + 2, +@@ -1126,12 +3266,12 @@ + return WLAN_STATUS_UNSPECIFIED_FAILURE; + } + +- if (os_memcmp_const(mic, ftie->mic, mic_len) != 0) { ++ if (os_memcmp_const(mic, fte_mic, mic_len) != 0) { + wpa_printf(MSG_DEBUG, "FT: Invalid MIC in FTIE"); + wpa_printf(MSG_DEBUG, "FT: addr=" MACSTR " auth_addr=" MACSTR, + MAC2STR(sm->addr), MAC2STR(sm->wpa_auth->addr)); + wpa_hexdump(MSG_MSGDUMP, "FT: Received MIC", +- ftie->mic, mic_len); ++ fte_mic, mic_len); + wpa_hexdump(MSG_MSGDUMP, "FT: Calculated MIC", mic, mic_len); + wpa_hexdump(MSG_MSGDUMP, "FT: MDIE", + parse.mdie - 2, parse.mdie_len + 2); +@@ -1142,6 +3282,32 @@ + return WLAN_STATUS_INVALID_FTIE; + } + ++#ifdef CONFIG_OCV ++ if (wpa_auth_uses_ocv(sm)) { ++ struct wpa_channel_info ci; ++ int tx_chanwidth; ++ int tx_seg1_idx; ++ ++ if (wpa_channel_info(sm->wpa_auth, &ci) != 0) { ++ wpa_printf(MSG_WARNING, ++ "Failed to get channel info to validate received OCI in (Re)Assoc Request"); ++ return WLAN_STATUS_UNSPECIFIED_FAILURE; ++ } ++ ++ if (get_sta_tx_parameters(sm, ++ channel_width_to_int(ci.chanwidth), ++ ci.seg1_idx, &tx_chanwidth, ++ &tx_seg1_idx) < 0) ++ return WLAN_STATUS_UNSPECIFIED_FAILURE; ++ ++ if (ocv_verify_tx_params(parse.oci, parse.oci_len, &ci, ++ tx_chanwidth, tx_seg1_idx) != 0) { ++ wpa_printf(MSG_WARNING, "%s", ocv_errorstr); ++ return WLAN_STATUS_UNSPECIFIED_FAILURE; ++ } ++ } ++#endif /* CONFIG_OCV */ ++ + return WLAN_STATUS_SUCCESS; + } + +@@ -1199,6 +3365,11 @@ + + wpa_hexdump(MSG_MSGDUMP, "FT: Action frame body", ies, ies_len); + ++ if (!sm->wpa_auth->conf.ft_over_ds) { ++ wpa_printf(MSG_DEBUG, "FT: Over-DS option disabled - reject"); ++ return -1; ++ } ++ + /* RRB - Forward action frame to the target AP */ + frame = os_malloc(sizeof(*frame) + len); + if (frame == NULL) +@@ -1251,6 +3422,7 @@ + sm->ft_pending_cb = wpa_ft_rrb_rx_request_cb; + sm->ft_pending_cb_ctx = sm; + os_memcpy(sm->ft_pending_current_ap, current_ap, ETH_ALEN); ++ sm->ft_pending_pull_left_retries = sm->wpa_auth->conf.rkh_pull_retries; + res = wpa_ft_process_auth_req(sm, body, len, &resp_ies, + &resp_ies_len); + if (res < 0) { +@@ -1316,112 +3488,431 @@ + } + + ++static int wpa_ft_rrb_build_r0(const u8 *key, const size_t key_len, ++ const struct tlv_list *tlvs, ++ const struct wpa_ft_pmk_r0_sa *pmk_r0, ++ const u8 *r1kh_id, const u8 *s1kh_id, ++ const struct tlv_list *tlv_auth, ++ const u8 *src_addr, u8 type, ++ u8 **packet, size_t *packet_len) ++{ ++ u8 pmk_r1[PMK_LEN_MAX]; ++ size_t pmk_r1_len = pmk_r0->pmk_r0_len; ++ u8 pmk_r1_name[WPA_PMK_NAME_LEN]; ++ u8 f_pairwise[sizeof(le16)]; ++ u8 f_expires_in[sizeof(le16)]; ++ u8 f_session_timeout[sizeof(le32)]; ++ int expires_in; ++ int session_timeout; ++ struct os_reltime now; ++ int ret; ++ struct tlv_list sess_tlv[] = { ++ { .type = FT_RRB_PMK_R1, .len = pmk_r1_len, ++ .data = pmk_r1 }, ++ { .type = FT_RRB_PMK_R1_NAME, .len = sizeof(pmk_r1_name), ++ .data = pmk_r1_name }, ++ { .type = FT_RRB_PAIRWISE, .len = sizeof(f_pairwise), ++ .data = f_pairwise }, ++ { .type = FT_RRB_EXPIRES_IN, .len = sizeof(f_expires_in), ++ .data = f_expires_in }, ++ { .type = FT_RRB_IDENTITY, .len = pmk_r0->identity_len, ++ .data = pmk_r0->identity }, ++ { .type = FT_RRB_RADIUS_CUI, .len = pmk_r0->radius_cui_len, ++ .data = pmk_r0->radius_cui }, ++ { .type = FT_RRB_SESSION_TIMEOUT, ++ .len = sizeof(f_session_timeout), ++ .data = f_session_timeout }, ++ { .type = FT_RRB_LAST_EMPTY, .len = 0, .data = NULL }, ++ }; ++ ++ if (wpa_derive_pmk_r1(pmk_r0->pmk_r0, pmk_r0->pmk_r0_len, ++ pmk_r0->pmk_r0_name, r1kh_id, ++ s1kh_id, pmk_r1, pmk_r1_name) < 0) ++ return -1; ++ wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1 (for peer AP)", ++ pmk_r1, pmk_r1_len); ++ wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name (for peer AP)", ++ pmk_r1_name, WPA_PMK_NAME_LEN); ++ WPA_PUT_LE16(f_pairwise, pmk_r0->pairwise); ++ ++ os_get_reltime(&now); ++ if (pmk_r0->expiration > now.sec) ++ expires_in = pmk_r0->expiration - now.sec; ++ else if (pmk_r0->expiration) ++ expires_in = 1; ++ else ++ expires_in = 0; ++ WPA_PUT_LE16(f_expires_in, expires_in); ++ ++ if (pmk_r0->session_timeout > now.sec) ++ session_timeout = pmk_r0->session_timeout - now.sec; ++ else if (pmk_r0->session_timeout) ++ session_timeout = 1; ++ else ++ session_timeout = 0; ++ WPA_PUT_LE32(f_session_timeout, session_timeout); ++ ++ ret = wpa_ft_rrb_build(key, key_len, tlvs, sess_tlv, tlv_auth, ++ pmk_r0->vlan, src_addr, type, ++ packet, packet_len); ++ ++ os_memset(pmk_r1, 0, sizeof(pmk_r1)); ++ ++ return ret; ++} ++ ++ + static int wpa_ft_rrb_rx_pull(struct wpa_authenticator *wpa_auth, + const u8 *src_addr, +- const u8 *data, size_t data_len) ++ const u8 *enc, size_t enc_len, ++ const u8 *auth, size_t auth_len, ++ int no_defer) + { +- struct ft_r0kh_r1kh_pull_frame f; +- const u8 *crypt; +- u8 *plain; +- struct ft_remote_r1kh *r1kh; +- struct ft_r0kh_r1kh_resp_frame resp, r; +- u8 pmk_r0[PMK_LEN]; +- int pairwise; ++ const char *msgtype = "pull request"; ++ u8 *plain = NULL, *packet = NULL; ++ size_t plain_len = 0, packet_len = 0; ++ struct ft_remote_r1kh *r1kh, *r1kh_wildcard; ++ const u8 *key; ++ size_t key_len; ++ int seq_ret; ++ const u8 *f_nonce, *f_r0kh_id, *f_r1kh_id, *f_s1kh_id, *f_pmk_r0_name; ++ size_t f_nonce_len, f_r0kh_id_len, f_r1kh_id_len, f_s1kh_id_len; ++ size_t f_pmk_r0_name_len; ++ const struct wpa_ft_pmk_r0_sa *r0; ++ int ret; ++ struct tlv_list resp[2]; ++ struct tlv_list resp_auth[5]; ++ struct ft_rrb_seq f_seq; + + wpa_printf(MSG_DEBUG, "FT: Received PMK-R1 pull"); + +- if (data_len < sizeof(f)) +- return -1; ++ RRB_GET_AUTH(FT_RRB_R0KH_ID, r0kh_id, msgtype, -1); ++ wpa_hexdump(MSG_DEBUG, "FT: R0KH-ID", f_r0kh_id, f_r0kh_id_len); + +- r1kh = wpa_auth->conf.r1kh_list; +- while (r1kh) { +- if (os_memcmp(r1kh->addr, src_addr, ETH_ALEN) == 0) +- break; +- r1kh = r1kh->next; ++ if (wpa_ft_rrb_check_r0kh(wpa_auth, f_r0kh_id, f_r0kh_id_len)) { ++ wpa_printf(MSG_DEBUG, "FT: R0KH-ID mismatch"); ++ goto out; + } +- 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; ++ ++ RRB_GET_AUTH(FT_RRB_R1KH_ID, r1kh_id, msgtype, FT_R1KH_ID_LEN); ++ wpa_printf(MSG_DEBUG, "FT: R1KH-ID=" MACSTR, MAC2STR(f_r1kh_id)); ++ ++ wpa_ft_rrb_lookup_r1kh(wpa_auth, f_r1kh_id, &r1kh, &r1kh_wildcard); ++ if (r1kh) { ++ key = r1kh->key; ++ key_len = sizeof(r1kh->key); ++ } else if (r1kh_wildcard) { ++ wpa_printf(MSG_DEBUG, "FT: Using wildcard R1KH-ID"); ++ key = r1kh_wildcard->key; ++ key_len = sizeof(r1kh_wildcard->key); ++ } else { ++ goto out; + } + +- crypt = data + offsetof(struct ft_r0kh_r1kh_pull_frame, nonce); +- os_memset(&f, 0, sizeof(f)); +- plain = ((u8 *) &f) + offsetof(struct ft_r0kh_r1kh_pull_frame, nonce); +- /* aes_unwrap() does not support inplace decryption, so use a temporary +- * buffer for the data. */ +- if (aes_unwrap(r1kh->key, sizeof(r1kh->key), +- (FT_R0KH_R1KH_PULL_DATA_LEN + 7) / 8, +- crypt, plain) < 0) { +- wpa_printf(MSG_DEBUG, "FT: Failed to decrypt PMK-R1 pull " +- "request from " MACSTR, MAC2STR(src_addr)); +- return -1; ++ RRB_GET_AUTH(FT_RRB_NONCE, nonce, "pull request", FT_RRB_NONCE_LEN); ++ wpa_hexdump(MSG_DEBUG, "FT: nonce", f_nonce, f_nonce_len); ++ ++ seq_ret = FT_RRB_SEQ_DROP; ++ if (r1kh) ++ seq_ret = wpa_ft_rrb_seq_chk(r1kh->seq, src_addr, enc, enc_len, ++ auth, auth_len, msgtype, no_defer); ++ if (!no_defer && r1kh_wildcard && ++ (!r1kh || os_memcmp(r1kh->addr, src_addr, ETH_ALEN) != 0)) { ++ /* wildcard: r1kh-id unknown or changed addr -> do a seq req */ ++ seq_ret = FT_RRB_SEQ_DEFER; + } + +- 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)); ++ if (seq_ret == FT_RRB_SEQ_DROP) ++ goto out; + +- 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); ++ if (wpa_ft_rrb_decrypt(key, key_len, enc, enc_len, auth, auth_len, ++ src_addr, FT_PACKET_R0KH_R1KH_PULL, ++ &plain, &plain_len) < 0) ++ goto out; + +- /* 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; ++ if (!r1kh) ++ r1kh = wpa_ft_rrb_add_r1kh(wpa_auth, r1kh_wildcard, src_addr, ++ f_r1kh_id, ++ wpa_auth->conf.rkh_pos_timeout); ++ if (!r1kh) ++ goto out; ++ ++ if (seq_ret == FT_RRB_SEQ_DEFER) { ++ wpa_ft_rrb_seq_req(wpa_auth, r1kh->seq, src_addr, f_r0kh_id, ++ f_r0kh_id_len, f_r1kh_id, key, key_len, ++ enc, enc_len, auth, auth_len, ++ &wpa_ft_rrb_rx_pull); ++ goto out; + } + +- wpa_derive_pmk_r1(pmk_r0, f.pmk_r0_name, f.r1kh_id, f.s1kh_id, +- r.pmk_r1, r.pmk_r1_name); +- wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1", r.pmk_r1, PMK_LEN); +- wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name", r.pmk_r1_name, +- WPA_PMK_NAME_LEN); +- r.pairwise = host_to_le16(pairwise); +- os_memset(r.pad, 0, sizeof(r.pad)); ++ wpa_ft_rrb_seq_accept(wpa_auth, r1kh->seq, src_addr, auth, auth_len, ++ msgtype); ++ wpa_ft_rrb_r1kh_replenish(wpa_auth, r1kh, ++ wpa_auth->conf.rkh_pos_timeout); + +- if (aes_wrap(r1kh->key, sizeof(r1kh->key), +- (FT_R0KH_R1KH_RESP_DATA_LEN + 7) / 8, +- r.nonce, resp.nonce) < 0) { +- os_memset(pmk_r0, 0, PMK_LEN); +- return -1; ++ RRB_GET(FT_RRB_PMK_R0_NAME, pmk_r0_name, msgtype, WPA_PMK_NAME_LEN); ++ wpa_hexdump(MSG_DEBUG, "FT: PMKR0Name", f_pmk_r0_name, ++ f_pmk_r0_name_len); ++ ++ RRB_GET(FT_RRB_S1KH_ID, s1kh_id, msgtype, ETH_ALEN); ++ wpa_printf(MSG_DEBUG, "FT: S1KH-ID=" MACSTR, MAC2STR(f_s1kh_id)); ++ ++ if (wpa_ft_new_seq(r1kh->seq, &f_seq) < 0) { ++ wpa_printf(MSG_DEBUG, "FT: Failed to get seq num"); ++ goto out; + } + +- os_memset(pmk_r0, 0, PMK_LEN); ++ resp[0].type = FT_RRB_S1KH_ID; ++ resp[0].len = f_s1kh_id_len; ++ resp[0].data = f_s1kh_id; ++ resp[1].type = FT_RRB_LAST_EMPTY; ++ resp[1].len = 0; ++ resp[1].data = NULL; + +- wpa_ft_rrb_send(wpa_auth, src_addr, (u8 *) &resp, sizeof(resp)); ++ resp_auth[0].type = FT_RRB_NONCE; ++ resp_auth[0].len = f_nonce_len; ++ resp_auth[0].data = f_nonce; ++ resp_auth[1].type = FT_RRB_SEQ; ++ resp_auth[1].len = sizeof(f_seq); ++ resp_auth[1].data = (u8 *) &f_seq; ++ resp_auth[2].type = FT_RRB_R0KH_ID; ++ resp_auth[2].len = f_r0kh_id_len; ++ resp_auth[2].data = f_r0kh_id; ++ resp_auth[3].type = FT_RRB_R1KH_ID; ++ resp_auth[3].len = f_r1kh_id_len; ++ resp_auth[3].data = f_r1kh_id; ++ resp_auth[4].type = FT_RRB_LAST_EMPTY; ++ resp_auth[4].len = 0; ++ resp_auth[4].data = NULL; + ++ if (wpa_ft_fetch_pmk_r0(wpa_auth, f_s1kh_id, f_pmk_r0_name, &r0) < 0) { ++ wpa_printf(MSG_DEBUG, "FT: No matching PMK-R0-Name found"); ++ ret = wpa_ft_rrb_build(key, key_len, resp, NULL, resp_auth, ++ NULL, wpa_auth->addr, ++ FT_PACKET_R0KH_R1KH_RESP, ++ &packet, &packet_len); ++ } else { ++ ret = wpa_ft_rrb_build_r0(key, key_len, resp, r0, f_r1kh_id, ++ f_s1kh_id, resp_auth, wpa_auth->addr, ++ FT_PACKET_R0KH_R1KH_RESP, ++ &packet, &packet_len); ++ } ++ ++ if (!ret) ++ wpa_ft_rrb_oui_send(wpa_auth, src_addr, ++ FT_PACKET_R0KH_R1KH_RESP, packet, ++ packet_len); ++ ++out: ++ os_free(plain); ++ os_free(packet); ++ + return 0; + } + + +-static void ft_pull_resp_cb_finish(void *eloop_ctx, void *timeout_ctx) ++/* @returns 0 on success ++ * -1 on error ++ * -2 if FR_RRB_PAIRWISE is missing ++ */ ++static int wpa_ft_rrb_rx_r1(struct wpa_authenticator *wpa_auth, ++ const u8 *src_addr, u8 type, ++ const u8 *enc, size_t enc_len, ++ const u8 *auth, size_t auth_len, ++ const char *msgtype, u8 *s1kh_id_out, ++ int (*cb)(struct wpa_authenticator *wpa_auth, ++ const u8 *src_addr, ++ const u8 *enc, size_t enc_len, ++ const u8 *auth, size_t auth_len, ++ int no_defer)) + { +- struct wpa_state_machine *sm = eloop_ctx; ++ u8 *plain = NULL; ++ size_t plain_len = 0; ++ struct ft_remote_r0kh *r0kh, *r0kh_wildcard; ++ const u8 *key; ++ size_t key_len; ++ int seq_ret; ++ const u8 *f_r1kh_id, *f_s1kh_id, *f_r0kh_id; ++ const u8 *f_pmk_r1_name, *f_pairwise, *f_pmk_r1; ++ const u8 *f_expires_in; ++ size_t f_r1kh_id_len, f_s1kh_id_len, f_r0kh_id_len; ++ const u8 *f_identity, *f_radius_cui; ++ const u8 *f_session_timeout; ++ size_t f_pmk_r1_name_len, f_pairwise_len, f_pmk_r1_len; ++ size_t f_expires_in_len; ++ size_t f_identity_len, f_radius_cui_len; ++ size_t f_session_timeout_len; ++ int pairwise; ++ int ret = -1; ++ int expires_in; ++ int session_timeout; ++ struct vlan_description vlan; ++ size_t pmk_r1_len; ++ ++ RRB_GET_AUTH(FT_RRB_R0KH_ID, r0kh_id, msgtype, -1); ++ wpa_hexdump(MSG_DEBUG, "FT: R0KH-ID", f_r0kh_id, f_r0kh_id_len); ++ ++ RRB_GET_AUTH(FT_RRB_R1KH_ID, r1kh_id, msgtype, FT_R1KH_ID_LEN); ++ wpa_printf(MSG_DEBUG, "FT: R1KH-ID=" MACSTR, MAC2STR(f_r1kh_id)); ++ ++ if (wpa_ft_rrb_check_r1kh(wpa_auth, f_r1kh_id)) { ++ wpa_printf(MSG_DEBUG, "FT: R1KH-ID mismatch"); ++ goto out; ++ } ++ ++ wpa_ft_rrb_lookup_r0kh(wpa_auth, f_r0kh_id, f_r0kh_id_len, &r0kh, ++ &r0kh_wildcard); ++ if (r0kh) { ++ key = r0kh->key; ++ key_len = sizeof(r0kh->key); ++ } else if (r0kh_wildcard) { ++ wpa_printf(MSG_DEBUG, "FT: Using wildcard R0KH-ID"); ++ key = r0kh_wildcard->key; ++ key_len = sizeof(r0kh_wildcard->key); ++ } else { ++ goto out; ++ } ++ ++ seq_ret = FT_RRB_SEQ_DROP; ++ if (r0kh) { ++ seq_ret = wpa_ft_rrb_seq_chk(r0kh->seq, src_addr, enc, enc_len, ++ auth, auth_len, msgtype, ++ cb ? 0 : 1); ++ } ++ if (cb && r0kh_wildcard && ++ (!r0kh || os_memcmp(r0kh->addr, src_addr, ETH_ALEN) != 0)) { ++ /* wildcard: r0kh-id unknown or changed addr -> do a seq req */ ++ seq_ret = FT_RRB_SEQ_DEFER; ++ } ++ ++ if (seq_ret == FT_RRB_SEQ_DROP) ++ goto out; ++ ++ if (wpa_ft_rrb_decrypt(key, key_len, enc, enc_len, auth, auth_len, ++ src_addr, type, &plain, &plain_len) < 0) ++ goto out; ++ ++ if (!r0kh) ++ r0kh = wpa_ft_rrb_add_r0kh(wpa_auth, r0kh_wildcard, src_addr, ++ f_r0kh_id, f_r0kh_id_len, ++ wpa_auth->conf.rkh_pos_timeout); ++ if (!r0kh) ++ goto out; ++ ++ if (seq_ret == FT_RRB_SEQ_DEFER) { ++ wpa_ft_rrb_seq_req(wpa_auth, r0kh->seq, src_addr, f_r0kh_id, ++ f_r0kh_id_len, f_r1kh_id, key, key_len, ++ enc, enc_len, auth, auth_len, cb); ++ goto out; ++ } ++ ++ wpa_ft_rrb_seq_accept(wpa_auth, r0kh->seq, src_addr, auth, auth_len, ++ msgtype); ++ wpa_ft_rrb_r0kh_replenish(wpa_auth, r0kh, ++ wpa_auth->conf.rkh_pos_timeout); ++ ++ RRB_GET(FT_RRB_S1KH_ID, s1kh_id, msgtype, ETH_ALEN); ++ wpa_printf(MSG_DEBUG, "FT: S1KH-ID=" MACSTR, MAC2STR(f_s1kh_id)); ++ ++ if (s1kh_id_out) ++ os_memcpy(s1kh_id_out, f_s1kh_id, ETH_ALEN); ++ ++ ret = -2; ++ RRB_GET(FT_RRB_PAIRWISE, pairwise, msgtype, sizeof(le16)); ++ wpa_hexdump(MSG_DEBUG, "FT: pairwise", f_pairwise, f_pairwise_len); ++ ++ ret = -1; ++ RRB_GET(FT_RRB_PMK_R1_NAME, pmk_r1_name, msgtype, WPA_PMK_NAME_LEN); ++ wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name", ++ f_pmk_r1_name, WPA_PMK_NAME_LEN); ++ ++ pmk_r1_len = PMK_LEN; ++ if (wpa_ft_rrb_get_tlv(plain, plain_len, FT_RRB_PMK_R1, &f_pmk_r1_len, ++ &f_pmk_r1) == 0 && ++ (f_pmk_r1_len == PMK_LEN || f_pmk_r1_len == SHA384_MAC_LEN)) ++ pmk_r1_len = f_pmk_r1_len; ++ RRB_GET(FT_RRB_PMK_R1, pmk_r1, msgtype, pmk_r1_len); ++ wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1", f_pmk_r1, pmk_r1_len); ++ ++ pairwise = WPA_GET_LE16(f_pairwise); ++ ++ RRB_GET_OPTIONAL(FT_RRB_EXPIRES_IN, expires_in, msgtype, ++ sizeof(le16)); ++ if (f_expires_in) ++ expires_in = WPA_GET_LE16(f_expires_in); ++ else ++ expires_in = 0; ++ ++ wpa_printf(MSG_DEBUG, "FT: PMK-R1 %s - expires_in=%d", msgtype, ++ expires_in); ++ ++ if (wpa_ft_rrb_get_tlv_vlan(plain, plain_len, &vlan) < 0) { ++ wpa_printf(MSG_DEBUG, "FT: Cannot parse vlan"); ++ wpa_ft_rrb_dump(plain, plain_len); ++ goto out; ++ } ++ ++ wpa_printf(MSG_DEBUG, "FT: vlan %d%s", ++ le_to_host16(vlan.untagged), vlan.tagged[0] ? "+" : ""); ++ ++ RRB_GET_OPTIONAL(FT_RRB_IDENTITY, identity, msgtype, -1); ++ if (f_identity) ++ wpa_hexdump_ascii(MSG_DEBUG, "FT: Identity", f_identity, ++ f_identity_len); ++ ++ RRB_GET_OPTIONAL(FT_RRB_RADIUS_CUI, radius_cui, msgtype, -1); ++ if (f_radius_cui) ++ wpa_hexdump_ascii(MSG_DEBUG, "FT: CUI", f_radius_cui, ++ f_radius_cui_len); ++ ++ RRB_GET_OPTIONAL(FT_RRB_SESSION_TIMEOUT, session_timeout, msgtype, ++ sizeof(le32)); ++ if (f_session_timeout) ++ session_timeout = WPA_GET_LE32(f_session_timeout); ++ else ++ session_timeout = 0; ++ wpa_printf(MSG_DEBUG, "FT: session_timeout %d", session_timeout); ++ ++ if (wpa_ft_store_pmk_r1(wpa_auth, f_s1kh_id, f_pmk_r1, pmk_r1_len, ++ f_pmk_r1_name, ++ pairwise, &vlan, expires_in, session_timeout, ++ f_identity, f_identity_len, f_radius_cui, ++ f_radius_cui_len) < 0) ++ goto out; ++ ++ ret = 0; ++out: ++ if (plain) { ++ os_memset(plain, 0, plain_len); ++ os_free(plain); ++ } ++ ++ return ret; ++ ++} ++ ++ ++static void ft_finish_pull(struct wpa_state_machine *sm) ++{ + int res; + u8 *resp_ies; + size_t resp_ies_len; + u16 status; + ++ if (!sm->ft_pending_cb || !sm->ft_pending_req_ies) ++ return; ++ + res = wpa_ft_process_auth_req(sm, wpabuf_head(sm->ft_pending_req_ies), + wpabuf_len(sm->ft_pending_req_ies), + &resp_ies, &resp_ies_len); ++ if (res < 0) { ++ /* this loop is broken by ft_pending_pull_left_retries */ ++ wpa_printf(MSG_DEBUG, ++ "FT: Callback postponed until response is available"); ++ return; ++ } + wpabuf_free(sm->ft_pending_req_ies); + sm->ft_pending_req_ies = NULL; +- if (res < 0) +- res = WLAN_STATUS_UNSPECIFIED_FAILURE; + status = res; + wpa_printf(MSG_DEBUG, "FT: Postponed auth callback result for " MACSTR + " - status %u", MAC2STR(sm->addr), status); +@@ -1433,21 +3924,26 @@ + } + + +-static int ft_pull_resp_cb(struct wpa_state_machine *sm, void *ctx) ++struct ft_get_sta_ctx { ++ const u8 *nonce; ++ const u8 *s1kh_id; ++ struct wpa_state_machine *sm; ++}; ++ ++ ++static int ft_get_sta_cb(struct wpa_state_machine *sm, void *ctx) + { +- struct ft_r0kh_r1kh_resp_frame *frame = ctx; ++ struct ft_get_sta_ctx *info = ctx; + +- if (os_memcmp(frame->s1kh_id, sm->addr, ETH_ALEN) != 0) ++ if ((info->s1kh_id && ++ os_memcmp(info->s1kh_id, sm->addr, ETH_ALEN) != 0) || ++ os_memcmp(info->nonce, sm->ft_pending_pull_nonce, ++ FT_RRB_NONCE_LEN) != 0 || ++ sm->ft_pending_cb == NULL || sm->ft_pending_req_ies == NULL) + return 0; +- if (os_memcmp(frame->nonce, sm->ft_pending_pull_nonce, +- FT_R0KH_R1KH_PULL_NONCE_LEN) != 0) +- return 0; +- if (sm->ft_pending_cb == NULL || sm->ft_pending_req_ies == NULL) +- return 0; + +- wpa_printf(MSG_DEBUG, "FT: Response to a pending pull request for " +- MACSTR " - process from timeout", MAC2STR(sm->addr)); +- eloop_register_timeout(0, 0, ft_pull_resp_cb_finish, sm, NULL); ++ info->sm = sm; ++ + return 1; + } + +@@ -1454,153 +3950,360 @@ + + static int wpa_ft_rrb_rx_resp(struct wpa_authenticator *wpa_auth, + const u8 *src_addr, +- const u8 *data, size_t data_len) ++ const u8 *enc, size_t enc_len, ++ const u8 *auth, size_t auth_len, ++ int no_defer) + { +- struct ft_r0kh_r1kh_resp_frame f; +- const u8 *crypt; +- u8 *plain; +- struct ft_remote_r0kh *r0kh; +- int pairwise, res; ++ const char *msgtype = "pull response"; ++ int nak, ret = -1; ++ struct ft_get_sta_ctx ctx; ++ u8 s1kh_id[ETH_ALEN]; ++ const u8 *f_nonce; ++ size_t f_nonce_len; + + wpa_printf(MSG_DEBUG, "FT: Received PMK-R1 pull response"); + +- if (data_len < sizeof(f)) +- return -1; ++ RRB_GET_AUTH(FT_RRB_NONCE, nonce, msgtype, FT_RRB_NONCE_LEN); ++ wpa_hexdump(MSG_DEBUG, "FT: nonce", f_nonce, f_nonce_len); + +- 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)); ++ os_memset(&ctx, 0, sizeof(ctx)); ++ ctx.nonce = f_nonce; ++ if (!wpa_auth_for_each_sta(wpa_auth, ft_get_sta_cb, &ctx)) { ++ /* nonce not found */ ++ wpa_printf(MSG_DEBUG, "FT: Invalid nonce"); + return -1; + } + +- crypt = data + offsetof(struct ft_r0kh_r1kh_resp_frame, nonce); +- os_memset(&f, 0, sizeof(f)); +- plain = ((u8 *) &f) + offsetof(struct ft_r0kh_r1kh_resp_frame, nonce); +- /* aes_unwrap() does not support inplace decryption, so use a temporary +- * buffer for the data. */ +- if (aes_unwrap(r0kh->key, sizeof(r0kh->key), +- (FT_R0KH_R1KH_RESP_DATA_LEN + 7) / 8, +- crypt, plain) < 0) { +- wpa_printf(MSG_DEBUG, "FT: Failed to decrypt PMK-R1 pull " +- "response from " MACSTR, MAC2STR(src_addr)); ++ ret = wpa_ft_rrb_rx_r1(wpa_auth, src_addr, FT_PACKET_R0KH_R1KH_RESP, ++ enc, enc_len, auth, auth_len, msgtype, s1kh_id, ++ no_defer ? NULL : &wpa_ft_rrb_rx_resp); ++ if (ret == -2) { ++ ret = 0; ++ nak = 1; ++ } else { ++ nak = 0; ++ } ++ if (ret < 0) + return -1; +- } + +- if (os_memcmp_const(f.r1kh_id, wpa_auth->conf.r1_key_holder, +- FT_R1KH_ID_LEN) != 0) { +- wpa_printf(MSG_DEBUG, "FT: PMK-R1 pull response did not use a " +- "matching R1KH-ID"); +- return -1; ++ ctx.s1kh_id = s1kh_id; ++ if (wpa_auth_for_each_sta(wpa_auth, ft_get_sta_cb, &ctx)) { ++ wpa_printf(MSG_DEBUG, ++ "FT: Response to a pending pull request for " MACSTR, ++ MAC2STR(ctx.sm->addr)); ++ eloop_cancel_timeout(wpa_ft_expire_pull, ctx.sm, NULL); ++ if (nak) ++ ctx.sm->ft_pending_pull_left_retries = 0; ++ ft_finish_pull(ctx.sm); + } + +- pairwise = le_to_host16(f.pairwise); +- wpa_hexdump(MSG_DEBUG, "FT: PMK-R1 pull - nonce", +- f.nonce, sizeof(f.nonce)); +- wpa_printf(MSG_DEBUG, "FT: PMK-R1 pull - R1KH-ID=" MACSTR " S1KH-ID=" +- MACSTR " pairwise=0x%x", +- MAC2STR(f.r1kh_id), MAC2STR(f.s1kh_id), pairwise); +- wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1 pull - PMK-R1", +- f.pmk_r1, PMK_LEN); +- wpa_hexdump(MSG_DEBUG, "FT: PMK-R1 pull - PMKR1Name", +- f.pmk_r1_name, WPA_PMK_NAME_LEN); +- +- res = wpa_ft_store_pmk_r1(wpa_auth, f.s1kh_id, f.pmk_r1, f.pmk_r1_name, +- pairwise); +- wpa_printf(MSG_DEBUG, "FT: Look for pending pull request"); +- wpa_auth_for_each_sta(wpa_auth, ft_pull_resp_cb, &f); +- os_memset(f.pmk_r1, 0, PMK_LEN); +- +- return res ? 0 : -1; ++out: ++ return ret; + } + + + static int wpa_ft_rrb_rx_push(struct wpa_authenticator *wpa_auth, + const u8 *src_addr, +- const u8 *data, size_t data_len) ++ const u8 *enc, size_t enc_len, ++ const u8 *auth, size_t auth_len, int no_defer) + { +- struct ft_r0kh_r1kh_push_frame f; +- const u8 *crypt; +- u8 *plain; +- struct ft_remote_r0kh *r0kh; +- struct os_time now; +- os_time_t tsend; +- int pairwise; ++ const char *msgtype = "push"; + + wpa_printf(MSG_DEBUG, "FT: Received PMK-R1 push"); + +- if (data_len < sizeof(f)) ++ if (wpa_ft_rrb_rx_r1(wpa_auth, src_addr, FT_PACKET_R0KH_R1KH_PUSH, ++ enc, enc_len, auth, auth_len, msgtype, NULL, ++ no_defer ? NULL : wpa_ft_rrb_rx_push) < 0) + return -1; + +- r0kh = wpa_auth->conf.r0kh_list; +- while (r0kh) { +- if (os_memcmp(r0kh->addr, src_addr, ETH_ALEN) == 0) +- break; +- r0kh = r0kh->next; ++ return 0; ++} ++ ++ ++static int wpa_ft_rrb_rx_seq(struct wpa_authenticator *wpa_auth, ++ const u8 *src_addr, int type, ++ const u8 *enc, size_t enc_len, ++ const u8 *auth, size_t auth_len, ++ struct ft_remote_seq **rkh_seq, ++ u8 **key, size_t *key_len, ++ struct ft_remote_r0kh **r0kh_out, ++ struct ft_remote_r1kh **r1kh_out, ++ struct ft_remote_r0kh **r0kh_wildcard_out, ++ struct ft_remote_r1kh **r1kh_wildcard_out) ++{ ++ struct ft_remote_r0kh *r0kh = NULL; ++ struct ft_remote_r1kh *r1kh = NULL; ++ const u8 *f_r0kh_id, *f_r1kh_id; ++ size_t f_r0kh_id_len, f_r1kh_id_len; ++ int to_r0kh, to_r1kh; ++ u8 *plain = NULL; ++ size_t plain_len = 0; ++ struct ft_remote_r0kh *r0kh_wildcard; ++ struct ft_remote_r1kh *r1kh_wildcard; ++ ++ RRB_GET_AUTH(FT_RRB_R0KH_ID, r0kh_id, "seq", -1); ++ RRB_GET_AUTH(FT_RRB_R1KH_ID, r1kh_id, "seq", FT_R1KH_ID_LEN); ++ ++ to_r0kh = !wpa_ft_rrb_check_r0kh(wpa_auth, f_r0kh_id, f_r0kh_id_len); ++ to_r1kh = !wpa_ft_rrb_check_r1kh(wpa_auth, f_r1kh_id); ++ ++ if (to_r0kh && to_r1kh) { ++ wpa_printf(MSG_DEBUG, "FT: seq - local R0KH-ID and R1KH-ID"); ++ goto out; + } +- 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; ++ ++ if (!to_r0kh && !to_r1kh) { ++ wpa_printf(MSG_DEBUG, "FT: seq - remote R0KH-ID and R1KH-ID"); ++ goto out; + } + +- crypt = data + offsetof(struct ft_r0kh_r1kh_push_frame, timestamp); +- os_memset(&f, 0, sizeof(f)); +- plain = ((u8 *) &f) + offsetof(struct ft_r0kh_r1kh_push_frame, +- timestamp); +- /* aes_unwrap() does not support inplace decryption, so use a temporary +- * buffer for the data. */ +- if (aes_unwrap(r0kh->key, sizeof(r0kh->key), +- (FT_R0KH_R1KH_PUSH_DATA_LEN + 7) / 8, +- crypt, plain) < 0) { +- wpa_printf(MSG_DEBUG, "FT: Failed to decrypt PMK-R1 push from " +- MACSTR, MAC2STR(src_addr)); +- return -1; ++ if (!to_r0kh) { ++ wpa_ft_rrb_lookup_r0kh(wpa_auth, f_r0kh_id, f_r0kh_id_len, ++ &r0kh, &r0kh_wildcard); ++ if (!r0kh_wildcard && ++ (!r0kh || os_memcmp(r0kh->addr, src_addr, ETH_ALEN) != 0)) { ++ wpa_hexdump(MSG_DEBUG, "FT: Did not find R0KH-ID", ++ f_r0kh_id, f_r0kh_id_len); ++ goto out; ++ } ++ if (r0kh) { ++ *key = r0kh->key; ++ *key_len = sizeof(r0kh->key); ++ } else { ++ *key = r0kh_wildcard->key; ++ *key_len = sizeof(r0kh_wildcard->key); ++ } + } + +- 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 (!to_r1kh) { ++ wpa_ft_rrb_lookup_r1kh(wpa_auth, f_r1kh_id, &r1kh, ++ &r1kh_wildcard); ++ if (!r1kh_wildcard && ++ (!r1kh || os_memcmp(r1kh->addr, src_addr, ETH_ALEN) != 0)) { ++ wpa_hexdump(MSG_DEBUG, "FT: Did not find R1KH-ID", ++ f_r1kh_id, FT_R1KH_ID_LEN); ++ goto out; ++ } ++ if (r1kh) { ++ *key = r1kh->key; ++ *key_len = sizeof(r1kh->key); ++ } else { ++ *key = r1kh_wildcard->key; ++ *key_len = sizeof(r1kh_wildcard->key); ++ } + } + +- if (os_memcmp_const(f.r1kh_id, wpa_auth->conf.r1_key_holder, +- FT_R1KH_ID_LEN) != 0) { +- wpa_printf(MSG_DEBUG, "FT: PMK-R1 push did not use a matching " +- "R1KH-ID (received " MACSTR " own " MACSTR ")", +- MAC2STR(f.r1kh_id), +- MAC2STR(wpa_auth->conf.r1_key_holder)); +- return -1; ++ if (wpa_ft_rrb_decrypt(*key, *key_len, enc, enc_len, auth, auth_len, ++ src_addr, type, &plain, &plain_len) < 0) ++ goto out; ++ ++ os_free(plain); ++ ++ if (!to_r0kh) { ++ if (!r0kh) ++ r0kh = wpa_ft_rrb_add_r0kh(wpa_auth, r0kh_wildcard, ++ src_addr, f_r0kh_id, ++ f_r0kh_id_len, ++ ftRRBseqTimeout); ++ if (!r0kh) ++ goto out; ++ ++ wpa_ft_rrb_r0kh_replenish(wpa_auth, r0kh, ftRRBseqTimeout); ++ *rkh_seq = r0kh->seq; ++ if (r0kh_out) ++ *r0kh_out = r0kh; ++ if (r0kh_wildcard_out) ++ *r0kh_wildcard_out = r0kh_wildcard; + } + +- 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); ++ if (!to_r1kh) { ++ if (!r1kh) ++ r1kh = wpa_ft_rrb_add_r1kh(wpa_auth, r1kh_wildcard, ++ src_addr, f_r1kh_id, ++ ftRRBseqTimeout); ++ if (!r1kh) ++ goto out; + +- 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); ++ wpa_ft_rrb_r1kh_replenish(wpa_auth, r1kh, ftRRBseqTimeout); ++ *rkh_seq = r1kh->seq; ++ if (r1kh_out) ++ *r1kh_out = r1kh; ++ if (r1kh_wildcard_out) ++ *r1kh_wildcard_out = r1kh_wildcard; ++ } + + return 0; ++out: ++ return -1; + } + + ++static int wpa_ft_rrb_rx_seq_req(struct wpa_authenticator *wpa_auth, ++ const u8 *src_addr, ++ const u8 *enc, size_t enc_len, ++ const u8 *auth, size_t auth_len, ++ int no_defer) ++{ ++ int ret = -1; ++ struct ft_rrb_seq f_seq; ++ const u8 *f_nonce, *f_r0kh_id, *f_r1kh_id; ++ size_t f_nonce_len, f_r0kh_id_len, f_r1kh_id_len; ++ struct ft_remote_seq *rkh_seq = NULL; ++ u8 *packet = NULL, *key = NULL; ++ size_t packet_len = 0, key_len = 0; ++ struct tlv_list seq_resp_auth[5]; ++ ++ wpa_printf(MSG_DEBUG, "FT: Received sequence number request"); ++ ++ if (wpa_ft_rrb_rx_seq(wpa_auth, src_addr, FT_PACKET_R0KH_R1KH_SEQ_REQ, ++ enc, enc_len, auth, auth_len, &rkh_seq, &key, ++ &key_len, NULL, NULL, NULL, NULL) < 0) ++ goto out; ++ ++ RRB_GET_AUTH(FT_RRB_NONCE, nonce, "seq request", FT_RRB_NONCE_LEN); ++ wpa_hexdump(MSG_DEBUG, "FT: seq request - nonce", f_nonce, f_nonce_len); ++ ++ RRB_GET_AUTH(FT_RRB_R0KH_ID, r0kh_id, "seq", -1); ++ RRB_GET_AUTH(FT_RRB_R1KH_ID, r1kh_id, "seq", FT_R1KH_ID_LEN); ++ ++ if (wpa_ft_new_seq(rkh_seq, &f_seq) < 0) { ++ wpa_printf(MSG_DEBUG, "FT: Failed to get seq num"); ++ goto out; ++ } ++ ++ seq_resp_auth[0].type = FT_RRB_NONCE; ++ seq_resp_auth[0].len = f_nonce_len; ++ seq_resp_auth[0].data = f_nonce; ++ seq_resp_auth[1].type = FT_RRB_SEQ; ++ seq_resp_auth[1].len = sizeof(f_seq); ++ seq_resp_auth[1].data = (u8 *) &f_seq; ++ seq_resp_auth[2].type = FT_RRB_R0KH_ID; ++ seq_resp_auth[2].len = f_r0kh_id_len; ++ seq_resp_auth[2].data = f_r0kh_id; ++ seq_resp_auth[3].type = FT_RRB_R1KH_ID; ++ seq_resp_auth[3].len = FT_R1KH_ID_LEN; ++ seq_resp_auth[3].data = f_r1kh_id; ++ seq_resp_auth[4].type = FT_RRB_LAST_EMPTY; ++ seq_resp_auth[4].len = 0; ++ seq_resp_auth[4].data = NULL; ++ ++ if (wpa_ft_rrb_build(key, key_len, NULL, NULL, seq_resp_auth, NULL, ++ wpa_auth->addr, FT_PACKET_R0KH_R1KH_SEQ_RESP, ++ &packet, &packet_len) < 0) ++ goto out; ++ ++ wpa_ft_rrb_oui_send(wpa_auth, src_addr, ++ FT_PACKET_R0KH_R1KH_SEQ_RESP, packet, ++ packet_len); ++ ++out: ++ os_free(packet); ++ ++ return ret; ++} ++ ++ ++static int wpa_ft_rrb_rx_seq_resp(struct wpa_authenticator *wpa_auth, ++ const u8 *src_addr, ++ const u8 *enc, size_t enc_len, ++ const u8 *auth, size_t auth_len, ++ int no_defer) ++{ ++ u8 *key = NULL; ++ size_t key_len = 0; ++ struct ft_remote_r0kh *r0kh = NULL, *r0kh_wildcard = NULL; ++ struct ft_remote_r1kh *r1kh = NULL, *r1kh_wildcard = NULL; ++ const u8 *f_nonce, *f_seq; ++ size_t f_nonce_len, f_seq_len; ++ struct ft_remote_seq *rkh_seq = NULL; ++ struct ft_remote_item *item; ++ struct os_reltime now, now_remote; ++ int seq_ret, found; ++ const struct ft_rrb_seq *msg_both; ++ u32 msg_dom, msg_seq; ++ ++ wpa_printf(MSG_DEBUG, "FT: Received sequence number response"); ++ ++ if (wpa_ft_rrb_rx_seq(wpa_auth, src_addr, FT_PACKET_R0KH_R1KH_SEQ_RESP, ++ enc, enc_len, auth, auth_len, &rkh_seq, &key, ++ &key_len, &r0kh, &r1kh, &r0kh_wildcard, ++ &r1kh_wildcard) < 0) ++ goto out; ++ ++ RRB_GET_AUTH(FT_RRB_NONCE, nonce, "seq response", FT_RRB_NONCE_LEN); ++ wpa_hexdump(MSG_DEBUG, "FT: seq response - nonce", f_nonce, ++ f_nonce_len); ++ ++ found = 0; ++ dl_list_for_each(item, &rkh_seq->rx.queue, struct ft_remote_item, ++ list) { ++ if (os_memcmp_const(f_nonce, item->nonce, ++ FT_RRB_NONCE_LEN) != 0 || ++ os_get_reltime(&now) < 0 || ++ os_reltime_expired(&now, &item->nonce_ts, ftRRBseqTimeout)) ++ continue; ++ ++ found = 1; ++ break; ++ } ++ if (!found) { ++ wpa_printf(MSG_DEBUG, "FT: seq response - bad nonce"); ++ goto out; ++ } ++ ++ if (r0kh) { ++ wpa_ft_rrb_r0kh_replenish(wpa_auth, r0kh, ++ wpa_auth->conf.rkh_pos_timeout); ++ if (r0kh_wildcard) ++ os_memcpy(r0kh->addr, src_addr, ETH_ALEN); ++ } ++ ++ if (r1kh) { ++ wpa_ft_rrb_r1kh_replenish(wpa_auth, r1kh, ++ wpa_auth->conf.rkh_pos_timeout); ++ if (r1kh_wildcard) ++ os_memcpy(r1kh->addr, src_addr, ETH_ALEN); ++ } ++ ++ seq_ret = wpa_ft_rrb_seq_chk(rkh_seq, src_addr, enc, enc_len, auth, ++ auth_len, "seq response", 1); ++ if (seq_ret == FT_RRB_SEQ_OK) { ++ wpa_printf(MSG_DEBUG, "FT: seq response - valid seq number"); ++ wpa_ft_rrb_seq_accept(wpa_auth, rkh_seq, src_addr, auth, ++ auth_len, "seq response"); ++ } else { ++ wpa_printf(MSG_DEBUG, "FT: seq response - reset seq number"); ++ ++ RRB_GET_AUTH(FT_RRB_SEQ, seq, "seq response", ++ sizeof(*msg_both)); ++ msg_both = (const struct ft_rrb_seq *) f_seq; ++ ++ msg_dom = le_to_host32(msg_both->dom); ++ msg_seq = le_to_host32(msg_both->seq); ++ now_remote.sec = le_to_host32(msg_both->ts); ++ now_remote.usec = 0; ++ ++ rkh_seq->rx.num_last = 2; ++ rkh_seq->rx.dom = msg_dom; ++ rkh_seq->rx.offsetidx = 0; ++ /* Accept some older, possibly cached packets as well */ ++ rkh_seq->rx.last[0] = msg_seq - FT_REMOTE_SEQ_BACKLOG - ++ dl_list_len(&rkh_seq->rx.queue); ++ rkh_seq->rx.last[1] = msg_seq; ++ ++ /* local time - offset = remote time ++ * <=> local time - remote time = offset */ ++ os_reltime_sub(&now, &now_remote, &rkh_seq->rx.time_offset); ++ } ++ ++ wpa_ft_rrb_seq_flush(wpa_auth, rkh_seq, 1); ++ ++ return 0; ++out: ++ return -1; ++} ++ ++ + int wpa_ft_rrb_rx(struct wpa_authenticator *wpa_auth, const u8 *src_addr, + const u8 *data, size_t data_len) + { +@@ -1642,13 +4345,6 @@ + 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) { +@@ -1726,65 +4422,140 @@ + } + + +-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) ++void wpa_ft_rrb_oui_rx(struct wpa_authenticator *wpa_auth, const u8 *src_addr, ++ const u8 *dst_addr, u8 oui_suffix, const u8 *data, ++ size_t data_len) + { +- struct ft_r0kh_r1kh_push_frame frame, f; +- struct os_time now; +- const u8 *plain; +- u8 *crypt; ++ const u8 *auth, *enc; ++ size_t alen, elen; ++ int no_defer = 0; + +- 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); ++ wpa_printf(MSG_DEBUG, "FT: RRB-OUI received frame from remote AP " ++ MACSTR, MAC2STR(src_addr)); ++ wpa_printf(MSG_DEBUG, "FT: RRB-OUI frame - oui_suffix=%d", oui_suffix); ++ wpa_hexdump(MSG_MSGDUMP, "FT: RRB frame payload", data, data_len); + +- /* aes_wrap() does not support inplace encryption, so use a temporary +- * buffer for the data. */ +- os_memcpy(f.r1kh_id, r1kh->id, FT_R1KH_ID_LEN); +- os_memcpy(f.s1kh_id, s1kh_id, ETH_ALEN); +- os_memcpy(f.pmk_r0_name, pmk_r0->pmk_r0_name, WPA_PMK_NAME_LEN); +- wpa_derive_pmk_r1(pmk_r0->pmk_r0, pmk_r0->pmk_r0_name, r1kh->id, +- s1kh_id, f.pmk_r1, f.pmk_r1_name); +- wpa_printf(MSG_DEBUG, "FT: R1KH-ID " MACSTR, MAC2STR(r1kh->id)); +- wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1", f.pmk_r1, PMK_LEN); +- wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name", f.pmk_r1_name, +- WPA_PMK_NAME_LEN); +- os_get_time(&now); +- WPA_PUT_LE32(f.timestamp, now.sec); +- f.pairwise = host_to_le16(pairwise); +- os_memset(f.pad, 0, sizeof(f.pad)); +- plain = ((const u8 *) &f) + offsetof(struct ft_r0kh_r1kh_push_frame, +- timestamp); +- crypt = ((u8 *) &frame) + offsetof(struct ft_r0kh_r1kh_push_frame, +- timestamp); +- if (aes_wrap(r1kh->key, sizeof(r1kh->key), +- (FT_R0KH_R1KH_PUSH_DATA_LEN + 7) / 8, +- plain, crypt) < 0) ++ if (is_multicast_ether_addr(src_addr)) { ++ wpa_printf(MSG_DEBUG, ++ "FT: RRB-OUI received frame from multicast address " ++ MACSTR, MAC2STR(src_addr)); + return; ++ } + +- wpa_ft_rrb_send(wpa_auth, r1kh->addr, (u8 *) &frame, sizeof(frame)); ++ if (is_multicast_ether_addr(dst_addr)) { ++ wpa_printf(MSG_DEBUG, ++ "FT: RRB-OUI received frame from remote AP " MACSTR ++ " to multicast address " MACSTR, ++ MAC2STR(src_addr), MAC2STR(dst_addr)); ++ no_defer = 1; ++ } ++ ++ if (data_len < sizeof(u16)) { ++ wpa_printf(MSG_DEBUG, "FT: RRB-OUI frame too short"); ++ return; ++ } ++ ++ alen = WPA_GET_LE16(data); ++ if (data_len < sizeof(u16) + alen) { ++ wpa_printf(MSG_DEBUG, "FT: RRB-OUI frame too short"); ++ return; ++ } ++ ++ auth = data + sizeof(u16); ++ wpa_hexdump(MSG_MSGDUMP, "FT: Authenticated payload", auth, alen); ++ enc = data + sizeof(u16) + alen; ++ elen = data_len - sizeof(u16) - alen; ++ wpa_hexdump(MSG_MSGDUMP, "FT: Encrypted payload", enc, elen); ++ ++ switch (oui_suffix) { ++ case FT_PACKET_R0KH_R1KH_PULL: ++ wpa_ft_rrb_rx_pull(wpa_auth, src_addr, enc, elen, auth, alen, ++ no_defer); ++ break; ++ case FT_PACKET_R0KH_R1KH_RESP: ++ wpa_ft_rrb_rx_resp(wpa_auth, src_addr, enc, elen, auth, alen, ++ no_defer); ++ break; ++ case FT_PACKET_R0KH_R1KH_PUSH: ++ wpa_ft_rrb_rx_push(wpa_auth, src_addr, enc, elen, auth, alen, ++ no_defer); ++ break; ++ case FT_PACKET_R0KH_R1KH_SEQ_REQ: ++ wpa_ft_rrb_rx_seq_req(wpa_auth, src_addr, enc, elen, auth, alen, ++ no_defer); ++ break; ++ case FT_PACKET_R0KH_R1KH_SEQ_RESP: ++ wpa_ft_rrb_rx_seq_resp(wpa_auth, src_addr, enc, elen, auth, ++ alen, no_defer); ++ break; ++ } + } + + ++static int 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) ++{ ++ u8 *packet; ++ size_t packet_len; ++ struct ft_rrb_seq f_seq; ++ struct tlv_list push[] = { ++ { .type = FT_RRB_S1KH_ID, .len = ETH_ALEN, ++ .data = s1kh_id }, ++ { .type = FT_RRB_PMK_R0_NAME, .len = WPA_PMK_NAME_LEN, ++ .data = pmk_r0->pmk_r0_name }, ++ { .type = FT_RRB_LAST_EMPTY, .len = 0, .data = NULL }, ++ }; ++ struct tlv_list push_auth[] = { ++ { .type = FT_RRB_SEQ, .len = sizeof(f_seq), ++ .data = (u8 *) &f_seq }, ++ { .type = FT_RRB_R0KH_ID, ++ .len = wpa_auth->conf.r0_key_holder_len, ++ .data = wpa_auth->conf.r0_key_holder }, ++ { .type = FT_RRB_R1KH_ID, .len = FT_R1KH_ID_LEN, ++ .data = r1kh->id }, ++ { .type = FT_RRB_LAST_EMPTY, .len = 0, .data = NULL }, ++ }; ++ ++ if (wpa_ft_new_seq(r1kh->seq, &f_seq) < 0) { ++ wpa_printf(MSG_DEBUG, "FT: Failed to get seq num"); ++ return -1; ++ } ++ ++ if (wpa_ft_rrb_build_r0(r1kh->key, sizeof(r1kh->key), push, pmk_r0, ++ r1kh->id, s1kh_id, push_auth, wpa_auth->addr, ++ FT_PACKET_R0KH_R1KH_PUSH, ++ &packet, &packet_len) < 0) ++ return -1; ++ ++ wpa_ft_rrb_oui_send(wpa_auth, r1kh->addr, FT_PACKET_R0KH_R1KH_PUSH, ++ packet, packet_len); ++ ++ os_free(packet); ++ return 0; ++} ++ ++ + void wpa_ft_push_pmk_r1(struct wpa_authenticator *wpa_auth, const u8 *addr) + { +- struct wpa_ft_pmk_r0_sa *r0; ++ struct wpa_ft_pmk_cache *cache = wpa_auth->ft_pmk_cache; ++ struct wpa_ft_pmk_r0_sa *r0, *r0found = NULL; + struct ft_remote_r1kh *r1kh; + + if (!wpa_auth->conf.pmk_r1_push) + return; ++ if (!wpa_auth->conf.r1kh_list) ++ return; + +- r0 = wpa_auth->ft_pmk_cache->pmk_r0; +- while (r0) { +- if (os_memcmp(r0->spa, addr, ETH_ALEN) == 0) ++ dl_list_for_each(r0, &cache->pmk_r0, struct wpa_ft_pmk_r0_sa, list) { ++ if (os_memcmp(r0->spa, addr, ETH_ALEN) == 0) { ++ r0found = r0; + break; +- r0 = r0->next; ++ } + } + ++ r0 = r0found; + if (r0 == NULL || r0->pmk_r1_pushed) + return; + r0->pmk_r1_pushed = 1; +@@ -1792,11 +4563,14 @@ + 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; ++ for (r1kh = *wpa_auth->conf.r1kh_list; r1kh; r1kh = r1kh->next) { ++ if (is_zero_ether_addr(r1kh->addr) || ++ is_zero_ether_addr(r1kh->id)) ++ continue; ++ if (wpa_ft_rrb_init_r1kh_seq(r1kh) < 0) ++ continue; ++ wpa_ft_generate_pmk_r1(wpa_auth, r0, r1kh, addr); + } + } + +-#endif /* CONFIG_IEEE80211R */ ++#endif /* CONFIG_IEEE80211R_AP */ +--- contrib/wpa/src/ap/wpa_auth_glue.c.orig ++++ contrib/wpa/src/ap/wpa_auth_glue.c +@@ -9,13 +9,17 @@ + #include "utils/includes.h" + + #include "utils/common.h" ++#include "utils/eloop.h" ++#include "utils/list.h" + #include "common/ieee802_11_defs.h" + #include "common/sae.h" + #include "common/wpa_ctrl.h" ++#include "crypto/sha1.h" + #include "eapol_auth/eapol_auth_sm.h" + #include "eapol_auth/eapol_auth_sm_i.h" + #include "eap_server/eap.h" + #include "l2_packet/l2_packet.h" ++#include "eth_p_oui.h" + #include "hostapd.h" + #include "ieee802_1x.h" + #include "preauth_auth.h" +@@ -23,6 +27,8 @@ + #include "tkip_countermeasures.h" + #include "ap_drv_ops.h" + #include "ap_config.h" ++#include "ieee802_11.h" ++#include "pmksa_cache_auth.h" + #include "wpa_auth.h" + #include "wpa_auth_glue.h" + +@@ -40,19 +46,26 @@ + wconf->wpa_strict_rekey = conf->wpa_strict_rekey; + wconf->wpa_gmk_rekey = conf->wpa_gmk_rekey; + wconf->wpa_ptk_rekey = conf->wpa_ptk_rekey; ++ wconf->wpa_group_update_count = conf->wpa_group_update_count; ++ wconf->wpa_disable_eapol_key_retries = ++ conf->wpa_disable_eapol_key_retries; ++ wconf->wpa_pairwise_update_count = conf->wpa_pairwise_update_count; + wconf->rsn_pairwise = conf->rsn_pairwise; + wconf->rsn_preauth = conf->rsn_preauth; + wconf->eapol_version = conf->eapol_version; +- wconf->peerkey = conf->peerkey; + wconf->wmm_enabled = conf->wmm_enabled; + wconf->wmm_uapsd = conf->wmm_uapsd; + wconf->disable_pmksa_caching = conf->disable_pmksa_caching; ++#ifdef CONFIG_OCV ++ wconf->ocv = conf->ocv; ++#endif /* CONFIG_OCV */ + wconf->okc = conf->okc; + #ifdef CONFIG_IEEE80211W + wconf->ieee80211w = conf->ieee80211w; + wconf->group_mgmt_cipher = conf->group_mgmt_cipher; ++ wconf->sae_require_mfp = conf->sae_require_mfp; + #endif /* CONFIG_IEEE80211W */ +-#ifdef CONFIG_IEEE80211R ++#ifdef CONFIG_IEEE80211R_AP + wconf->ssid_len = conf->ssid.ssid_len; + if (wconf->ssid_len > SSID_MAX_LEN) + wconf->ssid_len = SSID_MAX_LEN; +@@ -67,12 +80,18 @@ + } + os_memcpy(wconf->r1_key_holder, conf->r1_key_holder, FT_R1KH_ID_LEN); + wconf->r0_key_lifetime = conf->r0_key_lifetime; ++ wconf->r1_max_key_lifetime = conf->r1_max_key_lifetime; + wconf->reassociation_deadline = conf->reassociation_deadline; +- wconf->r0kh_list = conf->r0kh_list; +- wconf->r1kh_list = conf->r1kh_list; ++ wconf->rkh_pos_timeout = conf->rkh_pos_timeout; ++ wconf->rkh_neg_timeout = conf->rkh_neg_timeout; ++ wconf->rkh_pull_timeout = conf->rkh_pull_timeout; ++ wconf->rkh_pull_retries = conf->rkh_pull_retries; ++ wconf->r0kh_list = &conf->r0kh_list; ++ wconf->r1kh_list = &conf->r1kh_list; + wconf->pmk_r1_push = conf->pmk_r1_push; + wconf->ft_over_ds = conf->ft_over_ds; +-#endif /* CONFIG_IEEE80211R */ ++ wconf->ft_psk_generate_local = conf->ft_psk_generate_local; ++#endif /* CONFIG_IEEE80211R_AP */ + #ifdef CONFIG_HS20 + wconf->disable_gtk = conf->disable_dgaf; + if (conf->osen) { +@@ -106,6 +125,11 @@ + os_memcpy(wconf->ip_addr_start, conf->ip_addr_start, 4); + os_memcpy(wconf->ip_addr_end, conf->ip_addr_end, 4); + #endif /* CONFIG_P2P */ ++#ifdef CONFIG_FILS ++ wconf->fils_cache_id_set = conf->fils_cache_id_set; ++ os_memcpy(wconf->fils_cache_id, conf->fils_cache_id, ++ FILS_CACHE_ID_LEN); ++#endif /* CONFIG_FILS */ + } + + +@@ -222,12 +246,18 @@ + + static const u8 * hostapd_wpa_auth_get_psk(void *ctx, const u8 *addr, + const u8 *p2p_dev_addr, +- const u8 *prev_psk) ++ const u8 *prev_psk, size_t *psk_len, ++ int *vlan_id) + { + struct hostapd_data *hapd = ctx; + struct sta_info *sta = ap_get_sta(hapd, addr); + const u8 *psk; + ++ if (vlan_id) ++ *vlan_id = 0; ++ if (psk_len) ++ *psk_len = PMK_LEN; ++ + #ifdef CONFIG_SAE + if (sta && sta->auth_alg == WLAN_AUTH_SAE) { + if (!sta->sae || prev_psk) +@@ -234,9 +264,34 @@ + return NULL; + return sta->sae->pmk; + } ++ if (sta && wpa_auth_uses_sae(sta->wpa_sm)) { ++ wpa_printf(MSG_DEBUG, ++ "No PSK for STA trying to use SAE with PMKSA caching"); ++ return NULL; ++ } + #endif /* CONFIG_SAE */ + +- psk = hostapd_get_psk(hapd->conf, addr, p2p_dev_addr, prev_psk); ++#ifdef CONFIG_OWE ++ if ((hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_OWE) && ++ sta && sta->owe_pmk) { ++ if (psk_len) ++ *psk_len = sta->owe_pmk_len; ++ return sta->owe_pmk; ++ } ++ if ((hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_OWE) && sta) { ++ struct rsn_pmksa_cache_entry *sa; ++ ++ sa = wpa_auth_sta_get_pmksa(sta->wpa_sm); ++ if (sa && sa->akmp == WPA_KEY_MGMT_OWE) { ++ if (psk_len) ++ *psk_len = sa->pmk_len; ++ return sa->pmk; ++ } ++ } ++#endif /* CONFIG_OWE */ ++ ++ psk = hostapd_get_psk(hapd->conf, addr, p2p_dev_addr, prev_psk, ++ vlan_id); + /* + * This is about to iterate over all psks, prev_psk gives the last + * returned psk which should not be returned again. +@@ -244,8 +299,18 @@ + */ + if (sta && sta->psk && !psk) { + struct hostapd_sta_wpa_psk_short *pos; ++ ++ if (vlan_id) ++ *vlan_id = 0; + psk = sta->psk->psk; + for (pos = sta->psk; pos; pos = pos->next) { ++ if (pos->is_passphrase) { ++ pbkdf2_sha1(pos->passphrase, ++ hapd->conf->ssid.ssid, ++ hapd->conf->ssid.ssid_len, 4096, ++ pos->psk, PMK_LEN); ++ pos->is_passphrase = 0; ++ } + if (pos->psk == prev_psk) { + psk = pos->next ? pos->next->psk : NULL; + break; +@@ -299,6 +364,37 @@ + return -1; + } + ++#ifdef CONFIG_TESTING_OPTIONS ++ if (addr && !is_broadcast_ether_addr(addr)) { ++ struct sta_info *sta; ++ ++ sta = ap_get_sta(hapd, addr); ++ if (sta) { ++ sta->last_tk_alg = alg; ++ sta->last_tk_key_idx = idx; ++ if (key) ++ os_memcpy(sta->last_tk, key, key_len); ++ sta->last_tk_len = key_len; ++ } ++#ifdef CONFIG_IEEE80211W ++ } else if (alg == WPA_ALG_IGTK || ++ alg == WPA_ALG_BIP_GMAC_128 || ++ alg == WPA_ALG_BIP_GMAC_256 || ++ alg == WPA_ALG_BIP_CMAC_256) { ++ hapd->last_igtk_alg = alg; ++ hapd->last_igtk_key_idx = idx; ++ if (key) ++ os_memcpy(hapd->last_igtk, key, key_len); ++ hapd->last_igtk_len = key_len; ++#endif /* CONFIG_IEEE80211W */ ++ } else { ++ hapd->last_gtk_alg = alg; ++ hapd->last_gtk_key_idx = idx; ++ if (key) ++ os_memcpy(hapd->last_gtk, key, key_len); ++ hapd->last_gtk_len = key_len; ++ } ++#endif /* CONFIG_TESTING_OPTIONS */ + return hostapd_drv_set_key(ifname, hapd, alg, addr, idx, 1, NULL, 0, + key, key_len); + } +@@ -393,8 +489,33 @@ + } + + +-#ifdef CONFIG_IEEE80211R ++#ifdef CONFIG_IEEE80211R_AP + ++struct wpa_ft_rrb_rx_later_data { ++ struct dl_list list; ++ u8 addr[ETH_ALEN]; ++ size_t data_len; ++ /* followed by data_len octets of data */ ++}; ++ ++static void hostapd_wpa_ft_rrb_rx_later(void *eloop_ctx, void *timeout_ctx) ++{ ++ struct hostapd_data *hapd = eloop_ctx; ++ struct wpa_ft_rrb_rx_later_data *data, *n; ++ ++ dl_list_for_each_safe(data, n, &hapd->l2_queue, ++ struct wpa_ft_rrb_rx_later_data, list) { ++ if (hapd->wpa_auth) { ++ wpa_ft_rrb_rx(hapd->wpa_auth, data->addr, ++ (const u8 *) (data + 1), ++ data->data_len); ++ } ++ dl_list_del(&data->list); ++ os_free(data); ++ } ++} ++ ++ + struct wpa_auth_ft_iface_iter_data { + struct hostapd_data *src_hapd; + const u8 *dst; +@@ -406,31 +527,54 @@ + static int hostapd_wpa_auth_ft_iter(struct hostapd_iface *iface, void *ctx) + { + struct wpa_auth_ft_iface_iter_data *idata = ctx; ++ struct wpa_ft_rrb_rx_later_data *data; + struct hostapd_data *hapd; + size_t j; + + for (j = 0; j < iface->num_bss; j++) { + hapd = iface->bss[j]; +- if (hapd == idata->src_hapd) ++ if (hapd == idata->src_hapd || ++ !hapd->wpa_auth || ++ os_memcmp(hapd->own_addr, idata->dst, ETH_ALEN) != 0) + continue; +- if (os_memcmp(hapd->own_addr, idata->dst, ETH_ALEN) == 0) { +- wpa_printf(MSG_DEBUG, "FT: Send RRB data directly to " +- "locally managed BSS " MACSTR "@%s -> " +- MACSTR "@%s", +- MAC2STR(idata->src_hapd->own_addr), +- idata->src_hapd->conf->iface, +- MAC2STR(hapd->own_addr), hapd->conf->iface); +- wpa_ft_rrb_rx(hapd->wpa_auth, +- idata->src_hapd->own_addr, +- idata->data, idata->data_len); ++ ++ wpa_printf(MSG_DEBUG, ++ "FT: Send RRB data directly to locally managed BSS " ++ MACSTR "@%s -> " MACSTR "@%s", ++ MAC2STR(idata->src_hapd->own_addr), ++ idata->src_hapd->conf->iface, ++ MAC2STR(hapd->own_addr), hapd->conf->iface); ++ ++ /* Defer wpa_ft_rrb_rx() until next eloop step as this is ++ * when it would be triggered when reading from a socket. ++ * This avoids ++ * hapd0:send -> hapd1:recv -> hapd1:send -> hapd0:recv, ++ * that is calling hapd0:recv handler from within ++ * hapd0:send directly. ++ */ ++ data = os_zalloc(sizeof(*data) + idata->data_len); ++ if (!data) + return 1; +- } ++ ++ os_memcpy(data->addr, idata->src_hapd->own_addr, ETH_ALEN); ++ os_memcpy(data + 1, idata->data, idata->data_len); ++ data->data_len = idata->data_len; ++ ++ dl_list_add(&hapd->l2_queue, &data->list); ++ ++ if (!eloop_is_timeout_registered(hostapd_wpa_ft_rrb_rx_later, ++ hapd, NULL)) ++ eloop_register_timeout(0, 0, ++ hostapd_wpa_ft_rrb_rx_later, ++ hapd, NULL); ++ ++ return 1; + } + + return 0; + } + +-#endif /* CONFIG_IEEE80211R */ ++#endif /* CONFIG_IEEE80211R_AP */ + + + static int hostapd_wpa_auth_send_ether(void *ctx, const u8 *dst, u16 proto, +@@ -455,7 +599,7 @@ + } + #endif /* CONFIG_TESTING_OPTIONS */ + +-#ifdef CONFIG_IEEE80211R ++#ifdef CONFIG_IEEE80211R_AP + if (proto == ETH_P_RRB && hapd->iface->interfaces && + hapd->iface->interfaces->for_each_interface) { + int res; +@@ -470,7 +614,7 @@ + if (res == 1) + return data_len; + } +-#endif /* CONFIG_IEEE80211R */ ++#endif /* CONFIG_IEEE80211R_AP */ + + if (hapd->driver && hapd->driver->send_ether) + return hapd->driver->send_ether(hapd->drv_priv, dst, +@@ -493,8 +637,226 @@ + } + + +-#ifdef CONFIG_IEEE80211R ++#ifdef CONFIG_ETH_P_OUI ++static struct eth_p_oui_ctx * hostapd_wpa_get_oui(struct hostapd_data *hapd, ++ u8 oui_suffix) ++{ ++ switch (oui_suffix) { ++#ifdef CONFIG_IEEE80211R_AP ++ case FT_PACKET_R0KH_R1KH_PULL: ++ return hapd->oui_pull; ++ case FT_PACKET_R0KH_R1KH_RESP: ++ return hapd->oui_resp; ++ case FT_PACKET_R0KH_R1KH_PUSH: ++ return hapd->oui_push; ++ case FT_PACKET_R0KH_R1KH_SEQ_REQ: ++ return hapd->oui_sreq; ++ case FT_PACKET_R0KH_R1KH_SEQ_RESP: ++ return hapd->oui_sresp; ++#endif /* CONFIG_IEEE80211R_AP */ ++ default: ++ return NULL; ++ } ++} ++#endif /* CONFIG_ETH_P_OUI */ + ++ ++#ifdef CONFIG_IEEE80211R_AP ++ ++struct oui_deliver_later_data { ++ struct dl_list list; ++ u8 src_addr[ETH_ALEN]; ++ u8 dst_addr[ETH_ALEN]; ++ size_t data_len; ++ u8 oui_suffix; ++ /* followed by data_len octets of data */ ++}; ++ ++static void hostapd_oui_deliver_later(void *eloop_ctx, void *timeout_ctx) ++{ ++ struct hostapd_data *hapd = eloop_ctx; ++ struct oui_deliver_later_data *data, *n; ++ struct eth_p_oui_ctx *oui_ctx; ++ ++ dl_list_for_each_safe(data, n, &hapd->l2_oui_queue, ++ struct oui_deliver_later_data, list) { ++ oui_ctx = hostapd_wpa_get_oui(hapd, data->oui_suffix); ++ if (hapd->wpa_auth && oui_ctx) { ++ eth_p_oui_deliver(oui_ctx, data->src_addr, ++ data->dst_addr, ++ (const u8 *) (data + 1), ++ data->data_len); ++ } ++ dl_list_del(&data->list); ++ os_free(data); ++ } ++} ++ ++ ++struct wpa_auth_oui_iface_iter_data { ++ struct hostapd_data *src_hapd; ++ const u8 *dst_addr; ++ const u8 *data; ++ size_t data_len; ++ u8 oui_suffix; ++}; ++ ++static int hostapd_wpa_auth_oui_iter(struct hostapd_iface *iface, void *ctx) ++{ ++ struct wpa_auth_oui_iface_iter_data *idata = ctx; ++ struct oui_deliver_later_data *data; ++ struct hostapd_data *hapd; ++ size_t j; ++ ++ for (j = 0; j < iface->num_bss; j++) { ++ hapd = iface->bss[j]; ++ if (hapd == idata->src_hapd) ++ continue; ++ if (!is_multicast_ether_addr(idata->dst_addr) && ++ os_memcmp(hapd->own_addr, idata->dst_addr, ETH_ALEN) != 0) ++ continue; ++ ++ /* defer eth_p_oui_deliver until next eloop step as this is ++ * when it would be triggerd from reading from sock ++ * This avoids ++ * hapd0:send -> hapd1:recv -> hapd1:send -> hapd0:recv, ++ * that is calling hapd0:recv handler from within ++ * hapd0:send directly. ++ */ ++ data = os_zalloc(sizeof(*data) + idata->data_len); ++ if (!data) ++ return 1; ++ ++ os_memcpy(data->src_addr, idata->src_hapd->own_addr, ETH_ALEN); ++ os_memcpy(data->dst_addr, idata->dst_addr, ETH_ALEN); ++ os_memcpy(data + 1, idata->data, idata->data_len); ++ data->data_len = idata->data_len; ++ data->oui_suffix = idata->oui_suffix; ++ ++ dl_list_add(&hapd->l2_oui_queue, &data->list); ++ ++ if (!eloop_is_timeout_registered(hostapd_oui_deliver_later, ++ hapd, NULL)) ++ eloop_register_timeout(0, 0, ++ hostapd_oui_deliver_later, ++ hapd, NULL); ++ ++ return 1; ++ } ++ ++ return 0; ++} ++ ++#endif /* CONFIG_IEEE80211R_AP */ ++ ++ ++static int hostapd_wpa_auth_send_oui(void *ctx, const u8 *dst, u8 oui_suffix, ++ const u8 *data, size_t data_len) ++{ ++#ifdef CONFIG_ETH_P_OUI ++ struct hostapd_data *hapd = ctx; ++ struct eth_p_oui_ctx *oui_ctx; ++ ++#ifdef CONFIG_IEEE80211R_AP ++ if (hapd->iface->interfaces && ++ hapd->iface->interfaces->for_each_interface) { ++ struct wpa_auth_oui_iface_iter_data idata; ++ int res; ++ ++ idata.src_hapd = hapd; ++ idata.dst_addr = dst; ++ idata.data = data; ++ idata.data_len = data_len; ++ idata.oui_suffix = oui_suffix; ++ res = hapd->iface->interfaces->for_each_interface( ++ hapd->iface->interfaces, hostapd_wpa_auth_oui_iter, ++ &idata); ++ if (res == 1) ++ return data_len; ++ } ++#endif /* CONFIG_IEEE80211R_AP */ ++ ++ oui_ctx = hostapd_wpa_get_oui(hapd, oui_suffix); ++ if (!oui_ctx) ++ return -1; ++ ++ return eth_p_oui_send(oui_ctx, hapd->own_addr, dst, data, data_len); ++#else /* CONFIG_ETH_P_OUI */ ++ return -1; ++#endif /* CONFIG_ETH_P_OUI */ ++} ++ ++ ++static int hostapd_channel_info(void *ctx, struct wpa_channel_info *ci) ++{ ++ struct hostapd_data *hapd = ctx; ++ ++ return hostapd_drv_channel_info(hapd, ci); ++} ++ ++ ++static int hostapd_wpa_auth_update_vlan(void *ctx, const u8 *addr, int vlan_id) ++{ ++#ifndef CONFIG_NO_VLAN ++ struct hostapd_data *hapd = ctx; ++ struct sta_info *sta; ++ struct vlan_description vlan_desc; ++ ++ sta = ap_get_sta(hapd, addr); ++ if (!sta) ++ return -1; ++ ++ os_memset(&vlan_desc, 0, sizeof(vlan_desc)); ++ vlan_desc.notempty = 1; ++ vlan_desc.untagged = vlan_id; ++ if (!hostapd_vlan_valid(hapd->conf->vlan, &vlan_desc)) { ++ wpa_printf(MSG_INFO, "Invalid VLAN ID %d in wpa_psk_file", ++ vlan_id); ++ return -1; ++ } ++ ++ if (ap_sta_set_vlan(hapd, sta, &vlan_desc) < 0) { ++ wpa_printf(MSG_INFO, ++ "Failed to assign VLAN ID %d from wpa_psk_file to " ++ MACSTR, vlan_id, MAC2STR(sta->addr)); ++ return -1; ++ } ++ ++ wpa_printf(MSG_INFO, ++ "Assigned VLAN ID %d from wpa_psk_file to " MACSTR, ++ vlan_id, MAC2STR(sta->addr)); ++ if ((sta->flags & WLAN_STA_ASSOC) && ++ ap_sta_bind_vlan(hapd, sta) < 0) ++ return -1; ++#endif /* CONFIG_NO_VLAN */ ++ ++ return 0; ++} ++ ++ ++#ifdef CONFIG_OCV ++static int hostapd_get_sta_tx_params(void *ctx, const u8 *addr, ++ int ap_max_chanwidth, int ap_seg1_idx, ++ int *bandwidth, int *seg1_idx) ++{ ++ struct hostapd_data *hapd = ctx; ++ struct sta_info *sta; ++ ++ sta = ap_get_sta(hapd, addr); ++ if (!sta) { ++ hostapd_wpa_auth_logger(hapd, addr, LOGGER_INFO, ++ "Failed to get STA info to validate received OCI"); ++ return -1; ++ } ++ ++ return get_tx_parameters(sta, ap_max_chanwidth, ap_seg1_idx, bandwidth, ++ seg1_idx); ++} ++#endif /* CONFIG_OCV */ ++ ++ ++#ifdef CONFIG_IEEE80211R_AP ++ + static int hostapd_wpa_auth_send_ft_action(void *ctx, const u8 *dst, + const u8 *data, size_t data_len) + { +@@ -531,6 +893,9 @@ + struct hostapd_data *hapd = ctx; + struct sta_info *sta; + ++ wpa_printf(MSG_DEBUG, "Add station entry for " MACSTR ++ " based on WPA authenticator callback", ++ MAC2STR(sta_addr)); + if (hostapd_add_sta_node(hapd, sta_addr, WLAN_AUTH_FT) < 0) + return NULL; + +@@ -537,6 +902,9 @@ + sta = ap_sta_add(hapd, sta_addr); + if (sta == NULL) + return NULL; ++ if (hapd->driver && hapd->driver->add_sta_node) ++ sta->added_unassoc = 1; ++ sta->ft_over_ds = 1; + if (sta->wpa_sm) { + sta->auth_alg = WLAN_AUTH_FT; + return sta->wpa_sm; +@@ -553,6 +921,244 @@ + } + + ++static int hostapd_wpa_auth_set_vlan(void *ctx, const u8 *sta_addr, ++ struct vlan_description *vlan) ++{ ++ struct hostapd_data *hapd = ctx; ++ struct sta_info *sta; ++ ++ sta = ap_get_sta(hapd, sta_addr); ++ if (!sta || !sta->wpa_sm) ++ return -1; ++ ++ if (vlan->notempty && ++ !hostapd_vlan_valid(hapd->conf->vlan, vlan)) { ++ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, ++ HOSTAPD_LEVEL_INFO, ++ "Invalid VLAN %d%s received from FT", ++ vlan->untagged, vlan->tagged[0] ? "+" : ""); ++ return -1; ++ } ++ ++ if (ap_sta_set_vlan(hapd, sta, vlan) < 0) ++ return -1; ++ /* Configure wpa_group for GTK but ignore error due to driver not ++ * knowing this STA. */ ++ ap_sta_bind_vlan(hapd, sta); ++ ++ if (sta->vlan_id) ++ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, ++ HOSTAPD_LEVEL_INFO, "VLAN ID %d", sta->vlan_id); ++ ++ return 0; ++} ++ ++ ++static int hostapd_wpa_auth_get_vlan(void *ctx, const u8 *sta_addr, ++ struct vlan_description *vlan) ++{ ++ struct hostapd_data *hapd = ctx; ++ struct sta_info *sta; ++ ++ sta = ap_get_sta(hapd, sta_addr); ++ if (!sta) ++ return -1; ++ ++ if (sta->vlan_desc) ++ *vlan = *sta->vlan_desc; ++ else ++ os_memset(vlan, 0, sizeof(*vlan)); ++ ++ return 0; ++} ++ ++ ++static int ++hostapd_wpa_auth_set_identity(void *ctx, const u8 *sta_addr, ++ const u8 *identity, size_t identity_len) ++{ ++ struct hostapd_data *hapd = ctx; ++ struct sta_info *sta; ++ ++ sta = ap_get_sta(hapd, sta_addr); ++ if (!sta) ++ return -1; ++ ++ os_free(sta->identity); ++ sta->identity = NULL; ++ ++ if (sta->eapol_sm) { ++ os_free(sta->eapol_sm->identity); ++ sta->eapol_sm->identity = NULL; ++ sta->eapol_sm->identity_len = 0; ++ } ++ ++ if (!identity_len) ++ return 0; ++ ++ /* sta->identity is NULL terminated */ ++ sta->identity = os_zalloc(identity_len + 1); ++ if (!sta->identity) ++ return -1; ++ os_memcpy(sta->identity, identity, identity_len); ++ ++ if (sta->eapol_sm) { ++ sta->eapol_sm->identity = os_zalloc(identity_len); ++ if (!sta->eapol_sm->identity) ++ return -1; ++ os_memcpy(sta->eapol_sm->identity, identity, identity_len); ++ sta->eapol_sm->identity_len = identity_len; ++ } ++ ++ return 0; ++} ++ ++ ++static size_t ++hostapd_wpa_auth_get_identity(void *ctx, const u8 *sta_addr, const u8 **buf) ++{ ++ struct hostapd_data *hapd = ctx; ++ struct sta_info *sta; ++ size_t len; ++ char *identity; ++ ++ sta = ap_get_sta(hapd, sta_addr); ++ if (!sta) ++ return 0; ++ ++ *buf = ieee802_1x_get_identity(sta->eapol_sm, &len); ++ if (*buf && len) ++ return len; ++ ++ if (!sta->identity) { ++ *buf = NULL; ++ return 0; ++ } ++ ++ identity = sta->identity; ++ len = os_strlen(identity); ++ *buf = (u8 *) identity; ++ ++ return len; ++} ++ ++ ++static int ++hostapd_wpa_auth_set_radius_cui(void *ctx, const u8 *sta_addr, ++ const u8 *radius_cui, size_t radius_cui_len) ++{ ++ struct hostapd_data *hapd = ctx; ++ struct sta_info *sta; ++ ++ sta = ap_get_sta(hapd, sta_addr); ++ if (!sta) ++ return -1; ++ ++ os_free(sta->radius_cui); ++ sta->radius_cui = NULL; ++ ++ if (sta->eapol_sm) { ++ wpabuf_free(sta->eapol_sm->radius_cui); ++ sta->eapol_sm->radius_cui = NULL; ++ } ++ ++ if (!radius_cui) ++ return 0; ++ ++ /* sta->radius_cui is NULL terminated */ ++ sta->radius_cui = os_zalloc(radius_cui_len + 1); ++ if (!sta->radius_cui) ++ return -1; ++ os_memcpy(sta->radius_cui, radius_cui, radius_cui_len); ++ ++ if (sta->eapol_sm) { ++ sta->eapol_sm->radius_cui = wpabuf_alloc_copy(radius_cui, ++ radius_cui_len); ++ if (!sta->eapol_sm->radius_cui) ++ return -1; ++ } ++ ++ return 0; ++} ++ ++ ++static size_t ++hostapd_wpa_auth_get_radius_cui(void *ctx, const u8 *sta_addr, const u8 **buf) ++{ ++ struct hostapd_data *hapd = ctx; ++ struct sta_info *sta; ++ struct wpabuf *b; ++ size_t len; ++ char *radius_cui; ++ ++ sta = ap_get_sta(hapd, sta_addr); ++ if (!sta) ++ return 0; ++ ++ b = ieee802_1x_get_radius_cui(sta->eapol_sm); ++ if (b) { ++ len = wpabuf_len(b); ++ *buf = wpabuf_head(b); ++ return len; ++ } ++ ++ if (!sta->radius_cui) { ++ *buf = NULL; ++ return 0; ++ } ++ ++ radius_cui = sta->radius_cui; ++ len = os_strlen(radius_cui); ++ *buf = (u8 *) radius_cui; ++ ++ return len; ++} ++ ++ ++static void hostapd_wpa_auth_set_session_timeout(void *ctx, const u8 *sta_addr, ++ int session_timeout) ++{ ++ struct hostapd_data *hapd = ctx; ++ struct sta_info *sta; ++ ++ sta = ap_get_sta(hapd, sta_addr); ++ if (!sta) ++ return; ++ ++ if (session_timeout) { ++ os_get_reltime(&sta->session_timeout); ++ sta->session_timeout.sec += session_timeout; ++ sta->session_timeout_set = 1; ++ ap_sta_session_timeout(hapd, sta, session_timeout); ++ } else { ++ sta->session_timeout_set = 0; ++ ap_sta_no_session_timeout(hapd, sta); ++ } ++} ++ ++ ++static int hostapd_wpa_auth_get_session_timeout(void *ctx, const u8 *sta_addr) ++{ ++ struct hostapd_data *hapd = ctx; ++ struct sta_info *sta; ++ struct os_reltime now, remaining; ++ ++ sta = ap_get_sta(hapd, sta_addr); ++ if (!sta || !sta->session_timeout_set) ++ return 0; ++ ++ os_get_reltime(&now); ++ if (os_reltime_before(&sta->session_timeout, &now)) { ++ /* already expired, return >0 as timeout was set */ ++ return 1; ++ } ++ ++ os_reltime_sub(&sta->session_timeout, &now, &remaining); ++ ++ return (remaining.sec > 0) ? remaining.sec : 1; ++} ++ ++ + static void hostapd_rrb_receive(void *ctx, const u8 *src_addr, const u8 *buf, + size_t len) + { +@@ -563,11 +1169,30 @@ + ethhdr = (struct l2_ethhdr *) buf; + wpa_printf(MSG_DEBUG, "FT: RRB received packet " MACSTR " -> " + MACSTR, MAC2STR(ethhdr->h_source), MAC2STR(ethhdr->h_dest)); ++ if (!is_multicast_ether_addr(ethhdr->h_dest) && ++ os_memcmp(hapd->own_addr, ethhdr->h_dest, ETH_ALEN) != 0) ++ return; + wpa_ft_rrb_rx(hapd->wpa_auth, ethhdr->h_source, buf + sizeof(*ethhdr), + len - sizeof(*ethhdr)); + } + + ++static void hostapd_rrb_oui_receive(void *ctx, const u8 *src_addr, ++ const u8 *dst_addr, u8 oui_suffix, ++ const u8 *buf, size_t len) ++{ ++ struct hostapd_data *hapd = ctx; ++ ++ wpa_printf(MSG_DEBUG, "FT: RRB received packet " MACSTR " -> " ++ MACSTR, MAC2STR(src_addr), MAC2STR(dst_addr)); ++ if (!is_multicast_ether_addr(dst_addr) && ++ os_memcmp(hapd->own_addr, dst_addr, ETH_ALEN) != 0) ++ return; ++ wpa_ft_rrb_oui_rx(hapd->wpa_auth, src_addr, dst_addr, oui_suffix, buf, ++ len); ++} ++ ++ + static int hostapd_wpa_auth_add_tspec(void *ctx, const u8 *sta_addr, + u8 *tspec_ie, size_t tspec_ielen) + { +@@ -575,13 +1200,99 @@ + return hostapd_add_tspec(hapd, sta_addr, tspec_ie, tspec_ielen); + } + +-#endif /* CONFIG_IEEE80211R */ + + ++static int hostapd_wpa_register_ft_oui(struct hostapd_data *hapd, ++ const char *ft_iface) ++{ ++ hapd->oui_pull = eth_p_oui_register(hapd, ft_iface, ++ FT_PACKET_R0KH_R1KH_PULL, ++ hostapd_rrb_oui_receive, hapd); ++ if (!hapd->oui_pull) ++ return -1; ++ ++ hapd->oui_resp = eth_p_oui_register(hapd, ft_iface, ++ FT_PACKET_R0KH_R1KH_RESP, ++ hostapd_rrb_oui_receive, hapd); ++ if (!hapd->oui_resp) ++ return -1; ++ ++ hapd->oui_push = eth_p_oui_register(hapd, ft_iface, ++ FT_PACKET_R0KH_R1KH_PUSH, ++ hostapd_rrb_oui_receive, hapd); ++ if (!hapd->oui_push) ++ return -1; ++ ++ hapd->oui_sreq = eth_p_oui_register(hapd, ft_iface, ++ FT_PACKET_R0KH_R1KH_SEQ_REQ, ++ hostapd_rrb_oui_receive, hapd); ++ if (!hapd->oui_sreq) ++ return -1; ++ ++ hapd->oui_sresp = eth_p_oui_register(hapd, ft_iface, ++ FT_PACKET_R0KH_R1KH_SEQ_RESP, ++ hostapd_rrb_oui_receive, hapd); ++ if (!hapd->oui_sresp) ++ return -1; ++ ++ return 0; ++} ++ ++ ++static void hostapd_wpa_unregister_ft_oui(struct hostapd_data *hapd) ++{ ++ eth_p_oui_unregister(hapd->oui_pull); ++ hapd->oui_pull = NULL; ++ eth_p_oui_unregister(hapd->oui_resp); ++ hapd->oui_resp = NULL; ++ eth_p_oui_unregister(hapd->oui_push); ++ hapd->oui_push = NULL; ++ eth_p_oui_unregister(hapd->oui_sreq); ++ hapd->oui_sreq = NULL; ++ eth_p_oui_unregister(hapd->oui_sresp); ++ hapd->oui_sresp = NULL; ++} ++#endif /* CONFIG_IEEE80211R_AP */ ++ ++ + int hostapd_setup_wpa(struct hostapd_data *hapd) + { + struct wpa_auth_config _conf; +- struct wpa_auth_callbacks cb; ++ static const struct wpa_auth_callbacks cb = { ++ .logger = hostapd_wpa_auth_logger, ++ .disconnect = hostapd_wpa_auth_disconnect, ++ .mic_failure_report = hostapd_wpa_auth_mic_failure_report, ++ .psk_failure_report = hostapd_wpa_auth_psk_failure_report, ++ .set_eapol = hostapd_wpa_auth_set_eapol, ++ .get_eapol = hostapd_wpa_auth_get_eapol, ++ .get_psk = hostapd_wpa_auth_get_psk, ++ .get_msk = hostapd_wpa_auth_get_msk, ++ .set_key = hostapd_wpa_auth_set_key, ++ .get_seqnum = hostapd_wpa_auth_get_seqnum, ++ .send_eapol = hostapd_wpa_auth_send_eapol, ++ .for_each_sta = hostapd_wpa_auth_for_each_sta, ++ .for_each_auth = hostapd_wpa_auth_for_each_auth, ++ .send_ether = hostapd_wpa_auth_send_ether, ++ .send_oui = hostapd_wpa_auth_send_oui, ++ .channel_info = hostapd_channel_info, ++ .update_vlan = hostapd_wpa_auth_update_vlan, ++#ifdef CONFIG_OCV ++ .get_sta_tx_params = hostapd_get_sta_tx_params, ++#endif /* CONFIG_OCV */ ++#ifdef CONFIG_IEEE80211R_AP ++ .send_ft_action = hostapd_wpa_auth_send_ft_action, ++ .add_sta = hostapd_wpa_auth_add_sta, ++ .add_tspec = hostapd_wpa_auth_add_tspec, ++ .set_vlan = hostapd_wpa_auth_set_vlan, ++ .get_vlan = hostapd_wpa_auth_get_vlan, ++ .set_identity = hostapd_wpa_auth_set_identity, ++ .get_identity = hostapd_wpa_auth_get_identity, ++ .set_radius_cui = hostapd_wpa_auth_set_radius_cui, ++ .get_radius_cui = hostapd_wpa_auth_get_radius_cui, ++ .set_session_timeout = hostapd_wpa_auth_set_session_timeout, ++ .get_session_timeout = hostapd_wpa_auth_get_session_timeout, ++#endif /* CONFIG_IEEE80211R_AP */ ++ }; + const u8 *wpa_ie; + size_t wpa_ie_len; + +@@ -590,28 +1301,7 @@ + _conf.tx_status = 1; + if (hapd->iface->drv_flags & WPA_DRIVER_FLAGS_AP_MLME) + _conf.ap_mlme = 1; +- os_memset(&cb, 0, sizeof(cb)); +- cb.ctx = hapd; +- cb.logger = hostapd_wpa_auth_logger; +- cb.disconnect = hostapd_wpa_auth_disconnect; +- cb.mic_failure_report = hostapd_wpa_auth_mic_failure_report; +- cb.psk_failure_report = hostapd_wpa_auth_psk_failure_report; +- cb.set_eapol = hostapd_wpa_auth_set_eapol; +- cb.get_eapol = hostapd_wpa_auth_get_eapol; +- cb.get_psk = hostapd_wpa_auth_get_psk; +- cb.get_msk = hostapd_wpa_auth_get_msk; +- cb.set_key = hostapd_wpa_auth_set_key; +- cb.get_seqnum = hostapd_wpa_auth_get_seqnum; +- cb.send_eapol = hostapd_wpa_auth_send_eapol; +- cb.for_each_sta = hostapd_wpa_auth_for_each_sta; +- cb.for_each_auth = hostapd_wpa_auth_for_each_auth; +- cb.send_ether = hostapd_wpa_auth_send_ether; +-#ifdef CONFIG_IEEE80211R +- cb.send_ft_action = hostapd_wpa_auth_send_ft_action; +- cb.add_sta = hostapd_wpa_auth_add_sta; +- cb.add_tspec = hostapd_wpa_auth_add_tspec; +-#endif /* CONFIG_IEEE80211R */ +- hapd->wpa_auth = wpa_init(hapd->own_addr, &_conf, &cb); ++ hapd->wpa_auth = wpa_init(hapd->own_addr, &_conf, &cb, hapd); + if (hapd->wpa_auth == NULL) { + wpa_printf(MSG_ERROR, "WPA initialization failed."); + return -1; +@@ -636,12 +1326,14 @@ + return -1; + } + +-#ifdef CONFIG_IEEE80211R +- if (!hostapd_drv_none(hapd) && hapd->conf->ft_over_ds && ++#ifdef CONFIG_IEEE80211R_AP ++ if (!hostapd_drv_none(hapd) && + wpa_key_mgmt_ft(hapd->conf->wpa_key_mgmt)) { +- hapd->l2 = l2_packet_init(hapd->conf->bridge[0] ? +- hapd->conf->bridge : +- hapd->conf->iface, NULL, ETH_P_RRB, ++ const char *ft_iface; ++ ++ ft_iface = hapd->conf->bridge[0] ? hapd->conf->bridge : ++ hapd->conf->iface; ++ hapd->l2 = l2_packet_init(ft_iface, NULL, ETH_P_RRB, + hostapd_rrb_receive, hapd, 1); + if (hapd->l2 == NULL && + (hapd->driver == NULL || +@@ -650,8 +1342,14 @@ + "interface"); + return -1; + } ++ ++ if (hostapd_wpa_register_ft_oui(hapd, ft_iface)) { ++ wpa_printf(MSG_ERROR, ++ "Failed to open ETH_P_OUI interface"); ++ return -1; ++ } + } +-#endif /* CONFIG_IEEE80211R */ ++#endif /* CONFIG_IEEE80211R_AP */ + + return 0; + +@@ -674,13 +1372,14 @@ + wpa_deinit(hapd->wpa_auth); + hapd->wpa_auth = NULL; + +- if (hostapd_set_privacy(hapd, 0)) { ++ if (hapd->drv_priv && hostapd_set_privacy(hapd, 0)) { + wpa_printf(MSG_DEBUG, "Could not disable " + "PrivacyInvoked for interface %s", + hapd->conf->iface); + } + +- if (hostapd_set_generic_elem(hapd, (u8 *) "", 0)) { ++ if (hapd->drv_priv && ++ hostapd_set_generic_elem(hapd, (u8 *) "", 0)) { + wpa_printf(MSG_DEBUG, "Could not remove generic " + "information element from interface %s", + hapd->conf->iface); +@@ -688,8 +1387,13 @@ + } + ieee802_1x_deinit(hapd); + +-#ifdef CONFIG_IEEE80211R ++#ifdef CONFIG_IEEE80211R_AP ++ eloop_cancel_timeout(hostapd_wpa_ft_rrb_rx_later, hapd, ELOOP_ALL_CTX); ++ hostapd_wpa_ft_rrb_rx_later(hapd, NULL); /* flush without delivering */ ++ eloop_cancel_timeout(hostapd_oui_deliver_later, hapd, ELOOP_ALL_CTX); ++ hostapd_oui_deliver_later(hapd, NULL); /* flush without delivering */ + l2_packet_deinit(hapd->l2); + hapd->l2 = NULL; +-#endif /* CONFIG_IEEE80211R */ ++ hostapd_wpa_unregister_ft_oui(hapd); ++#endif /* CONFIG_IEEE80211R_AP */ + } +--- contrib/wpa/src/ap/wpa_auth_i.h.orig ++++ contrib/wpa/src/ap/wpa_auth_i.h +@@ -9,18 +9,13 @@ + #ifndef WPA_AUTH_I_H + #define WPA_AUTH_I_H + ++#include "utils/list.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; +@@ -27,6 +22,7 @@ + + u8 addr[ETH_ALEN]; + u8 p2p_dev_addr[ETH_ALEN]; ++ u16 auth_alg; + + enum { + WPA_PTK_INITIALIZE, WPA_PTK_DISCONNECT, WPA_PTK_DISCONNECTED, +@@ -48,8 +44,9 @@ + Boolean AuthenticationRequest; + Boolean ReAuthenticationRequest; + Boolean Disconnect; +- int TimeoutCtr; +- int GTimeoutCtr; ++ u16 disconnect_reason; /* specific reason code to use with Disconnect */ ++ u32 TimeoutCtr; ++ u32 GTimeoutCtr; + Boolean TimeoutEvt; + Boolean EAPOLKeyReceived; + Boolean EAPOLKeyPairwise; +@@ -60,7 +57,9 @@ + u8 SNonce[WPA_NONCE_LEN]; + u8 alt_SNonce[WPA_NONCE_LEN]; + u8 alt_replay_counter[WPA_REPLAY_COUNTER_LEN]; +- u8 PMK[PMK_LEN]; ++ u8 PMK[PMK_LEN_MAX]; ++ unsigned int pmk_len; ++ u8 pmkid[PMKID_LEN]; /* valid if pmkid_set == 1 */ + struct wpa_ptk PTK; + Boolean PTK_valid; + Boolean pairwise_set; +@@ -88,11 +87,15 @@ + unsigned int rx_eapol_key_secure:1; + unsigned int update_snonce:1; + unsigned int alt_snonce_valid:1; +-#ifdef CONFIG_IEEE80211R ++#ifdef CONFIG_IEEE80211R_AP + unsigned int ft_completed:1; + unsigned int pmk_r1_name_valid:1; +-#endif /* CONFIG_IEEE80211R */ ++#endif /* CONFIG_IEEE80211R_AP */ + unsigned int is_wnmsleep:1; ++ unsigned int pmkid_set:1; ++#ifdef CONFIG_OCV ++ unsigned int ocv_enabled:1; ++#endif /* CONFIG_OCV */ + + u8 req_replay_counter[WPA_REPLAY_COUNTER_LEN]; + int req_replay_counter_used; +@@ -112,9 +115,12 @@ + u32 dot11RSNAStatsTKIPLocalMICFailures; + u32 dot11RSNAStatsTKIPRemoteMICFailures; + +-#ifdef CONFIG_IEEE80211R +- u8 xxkey[PMK_LEN]; /* PSK or the second 256 bits of MSK */ ++#ifdef CONFIG_IEEE80211R_AP ++ u8 xxkey[PMK_LEN_MAX]; /* PSK or the second 256 bits of MSK, or the ++ * first 384 bits of MSK */ + size_t xxkey_len; ++ u8 pmk_r1[PMK_LEN_MAX]; ++ unsigned int pmk_r1_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 */ +@@ -128,10 +134,11 @@ + const u8 *ies, size_t ies_len); + void *ft_pending_cb_ctx; + struct wpabuf *ft_pending_req_ies; +- u8 ft_pending_pull_nonce[FT_R0KH_R1KH_PULL_NONCE_LEN]; ++ u8 ft_pending_pull_nonce[FT_RRB_NONCE_LEN]; + u8 ft_pending_auth_transaction; + u8 ft_pending_current_ap[ETH_ALEN]; +-#endif /* CONFIG_IEEE80211R */ ++ int ft_pending_pull_left_retries; ++#endif /* CONFIG_IEEE80211R_AP */ + + int pending_1_of_4_timeout; + +@@ -138,6 +145,23 @@ + #ifdef CONFIG_P2P + u8 ip_addr[4]; + #endif /* CONFIG_P2P */ ++ ++#ifdef CONFIG_FILS ++ u8 fils_key_auth_sta[FILS_MAX_KEY_AUTH_LEN]; ++ u8 fils_key_auth_ap[FILS_MAX_KEY_AUTH_LEN]; ++ size_t fils_key_auth_len; ++ unsigned int fils_completed:1; ++#endif /* CONFIG_FILS */ ++ ++#ifdef CONFIG_DPP2 ++ struct wpabuf *dpp_z; ++#endif /* CONFIG_DPP2 */ ++ ++#ifdef CONFIG_TESTING_OPTIONS ++ void (*eapol_status_cb)(void *ctx1, void *ctx2); ++ void *eapol_status_cb_ctx1; ++ void *eapol_status_cb_ctx2; ++#endif /* CONFIG_TESTING_OPTIONS */ + }; + + +@@ -172,6 +196,7 @@ + #endif /* CONFIG_IEEE80211W */ + /* Number of references except those in struct wpa_group->next */ + unsigned int references; ++ unsigned int num_setup_iface; + }; + + +@@ -192,10 +217,9 @@ + unsigned int dot11RSNATKIPCounterMeasuresInvoked; + unsigned int dot11RSNA4WayHandshakeFailures; + +- struct wpa_stsl_negotiation *stsl_negotiations; +- + struct wpa_auth_config conf; +- struct wpa_auth_callbacks cb; ++ const struct wpa_auth_callbacks *cb; ++ void *cb_ctx; + + u8 *wpa_ie; + size_t wpa_ie_len; +@@ -211,6 +235,38 @@ + }; + + ++#ifdef CONFIG_IEEE80211R_AP ++ ++#define FT_REMOTE_SEQ_BACKLOG 16 ++struct ft_remote_seq_rx { ++ u32 dom; ++ struct os_reltime time_offset; /* local time - offset = remote time */ ++ ++ /* accepted sequence numbers: (offset ... offset + 0x40000000] ++ * (except those in last) ++ * dropped sequence numbers: (offset - 0x40000000 ... offset] ++ * all others trigger SEQ_REQ message (except first message) ++ */ ++ u32 last[FT_REMOTE_SEQ_BACKLOG]; ++ unsigned int num_last; ++ u32 offsetidx; ++ ++ struct dl_list queue; /* send nonces + rrb msgs awaiting seq resp */ ++}; ++ ++struct ft_remote_seq_tx { ++ u32 dom; /* non zero if initialized */ ++ u32 seq; ++}; ++ ++struct ft_remote_seq { ++ struct ft_remote_seq_rx rx; ++ struct ft_remote_seq_tx tx; ++}; ++ ++#endif /* CONFIG_IEEE80211R_AP */ ++ ++ + 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, +@@ -229,32 +285,19 @@ + int (*cb)(struct wpa_authenticator *a, void *ctx), + void *cb_ctx); + +-#ifdef CONFIG_PEERKEY +-int wpa_stsl_remove(struct wpa_authenticator *wpa_auth, +- struct wpa_stsl_negotiation *neg); +-void wpa_smk_error(struct wpa_authenticator *wpa_auth, +- struct wpa_state_machine *sm, +- const u8 *key_data, size_t key_data_len); +-void wpa_smk_m1(struct wpa_authenticator *wpa_auth, +- struct wpa_state_machine *sm, struct wpa_eapol_key *key, +- const u8 *key_data, size_t key_data_len); +-void wpa_smk_m3(struct wpa_authenticator *wpa_auth, +- struct wpa_state_machine *sm, struct wpa_eapol_key *key, +- const u8 *key_data, size_t key_data_len); +-#endif /* CONFIG_PEERKEY */ +- +-#ifdef CONFIG_IEEE80211R ++#ifdef CONFIG_IEEE80211R_AP + 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, ++int wpa_write_ftie(struct wpa_auth_config *conf, int use_sha384, ++ 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); ++int wpa_auth_derive_ptk_ft(struct wpa_state_machine *sm, struct wpa_ptk *ptk); + struct wpa_ft_pmk_cache * wpa_ft_pmk_cache_init(void); + void wpa_ft_pmk_cache_deinit(struct wpa_ft_pmk_cache *cache); + void wpa_ft_install_ptk(struct wpa_state_machine *sm); +-#endif /* CONFIG_IEEE80211R */ ++int wpa_ft_store_pmk_fils(struct wpa_state_machine *sm, const u8 *pmk_r0, ++ const u8 *pmk_r0_name); ++#endif /* CONFIG_IEEE80211R_AP */ + + #endif /* WPA_AUTH_I_H */ +--- contrib/wpa/src/ap/wpa_auth_ie.c.orig ++++ contrib/wpa/src/ap/wpa_auth_ie.c +@@ -1,6 +1,6 @@ + /* + * hostapd - WPA/RSN IE and KDE definitions +- * Copyright (c) 2004-2015, Jouni Malinen ++ * Copyright (c) 2004-2018, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. +@@ -164,18 +164,25 @@ + pos += RSN_SELECTOR_LEN; + num_suites++; + } +-#ifdef CONFIG_IEEE80211R ++#ifdef CONFIG_IEEE80211R_AP + if (conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X) { + RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_802_1X); + pos += RSN_SELECTOR_LEN; + num_suites++; + } ++#ifdef CONFIG_SHA384 ++ if (conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X_SHA384) { ++ RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_802_1X_SHA384); ++ pos += RSN_SELECTOR_LEN; ++ num_suites++; ++ } ++#endif /* CONFIG_SHA384 */ + if (conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_PSK) { + RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_PSK); + pos += RSN_SELECTOR_LEN; + num_suites++; + } +-#endif /* CONFIG_IEEE80211R */ ++#endif /* CONFIG_IEEE80211R_AP */ + #ifdef CONFIG_IEEE80211W + if (conf->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X_SHA256) { + RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_802_1X_SHA256); +@@ -210,6 +217,51 @@ + pos += RSN_SELECTOR_LEN; + num_suites++; + } ++#ifdef CONFIG_FILS ++ if (conf->wpa_key_mgmt & WPA_KEY_MGMT_FILS_SHA256) { ++ RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FILS_SHA256); ++ pos += RSN_SELECTOR_LEN; ++ num_suites++; ++ } ++ if (conf->wpa_key_mgmt & WPA_KEY_MGMT_FILS_SHA384) { ++ RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FILS_SHA384); ++ pos += RSN_SELECTOR_LEN; ++ num_suites++; ++ } ++#ifdef CONFIG_IEEE80211R_AP ++ if (conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_FILS_SHA256) { ++ RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_FILS_SHA256); ++ pos += RSN_SELECTOR_LEN; ++ num_suites++; ++ } ++ if (conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_FILS_SHA384) { ++ RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_FILS_SHA384); ++ pos += RSN_SELECTOR_LEN; ++ num_suites++; ++ } ++#endif /* CONFIG_IEEE80211R_AP */ ++#endif /* CONFIG_FILS */ ++#ifdef CONFIG_OWE ++ if (conf->wpa_key_mgmt & WPA_KEY_MGMT_OWE) { ++ RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_OWE); ++ pos += RSN_SELECTOR_LEN; ++ num_suites++; ++ } ++#endif /* CONFIG_OWE */ ++#ifdef CONFIG_DPP ++ if (conf->wpa_key_mgmt & WPA_KEY_MGMT_DPP) { ++ RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_DPP); ++ pos += RSN_SELECTOR_LEN; ++ num_suites++; ++ } ++#endif /* CONFIG_DPP */ ++#ifdef CONFIG_HS20 ++ if (conf->wpa_key_mgmt & WPA_KEY_MGMT_OSEN) { ++ RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_OSEN); ++ pos += RSN_SELECTOR_LEN; ++ num_suites++; ++ } ++#endif /* CONFIG_HS20 */ + + #ifdef CONFIG_RSN_TESTING + if (rsn_testing) { +@@ -230,8 +282,6 @@ + capab = 0; + if (conf->rsn_preauth) + capab |= WPA_CAPABILITY_PREAUTH; +- if (conf->peerkey) +- capab |= WPA_CAPABILITY_PEERKEY_ENABLED; + if (conf->wmm_enabled) { + /* 4 PTKSA replay counters when using WMM */ + capab |= (RSN_NUM_REPLAY_COUNTERS_16 << 2); +@@ -243,15 +293,19 @@ + capab |= WPA_CAPABILITY_MFPR; + } + #endif /* CONFIG_IEEE80211W */ ++#ifdef CONFIG_OCV ++ if (conf->ocv) ++ capab |= WPA_CAPABILITY_OCVC; ++#endif /* CONFIG_OCV */ + #ifdef CONFIG_RSN_TESTING + if (rsn_testing) +- capab |= BIT(8) | BIT(14) | BIT(15); ++ capab |= BIT(8) | BIT(15); + #endif /* CONFIG_RSN_TESTING */ + WPA_PUT_LE16(pos, capab); + pos += 2; + + if (pmkid) { +- if (pos + 2 + PMKID_LEN > buf + len) ++ if (2 + PMKID_LEN > buf + len - pos) + return -1; + /* PMKID Count */ + WPA_PUT_LE16(pos, 1); +@@ -263,7 +317,7 @@ + #ifdef CONFIG_IEEE80211W + if (conf->ieee80211w != NO_MGMT_FRAME_PROTECTION && + conf->group_mgmt_cipher != WPA_CIPHER_AES_128_CMAC) { +- if (pos + 2 + 4 > buf + len) ++ if (2 + 4 > buf + len - pos) + return -1; + if (pmkid == NULL) { + /* PMKID Count */ +@@ -364,6 +418,10 @@ + capab |= WPA_CAPABILITY_MFPR; + } + #endif /* CONFIG_IEEE80211W */ ++#ifdef CONFIG_OCV ++ if (conf->ocv) ++ capab |= WPA_CAPABILITY_OCVC; ++#endif /* CONFIG_OCV */ + WPA_PUT_LE16(eid, capab); + eid += 2; + +@@ -407,7 +465,7 @@ + return res; + pos += res; + } +-#ifdef CONFIG_IEEE80211R ++#ifdef CONFIG_IEEE80211R_AP + if (wpa_key_mgmt_ft(wpa_auth->conf.wpa_key_mgmt)) { + res = wpa_write_mdie(&wpa_auth->conf, pos, + buf + sizeof(buf) - pos); +@@ -415,7 +473,7 @@ + return res; + pos += res; + } +-#endif /* CONFIG_IEEE80211R */ ++#endif /* CONFIG_IEEE80211R_AP */ + if (wpa_auth->conf.wpa & WPA_PROTO_WPA) { + res = wpa_write_wpa_ie(&wpa_auth->conf, + pos, buf + sizeof(buf) - pos); +@@ -472,9 +530,10 @@ + + + int wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth, +- struct wpa_state_machine *sm, ++ struct wpa_state_machine *sm, int freq, + const u8 *wpa_ie, size_t wpa_ie_len, +- const u8 *mdie, size_t mdie_len) ++ const u8 *mdie, size_t mdie_len, ++ const u8 *owe_dh, size_t owe_dh_len) + { + struct wpa_ie_data data; + int ciphers, key_mgmt, res, version; +@@ -501,7 +560,24 @@ + + if (version == WPA_PROTO_RSN) { + res = wpa_parse_wpa_ie_rsn(wpa_ie, wpa_ie_len, &data); ++ if (!data.has_pairwise) ++ data.pairwise_cipher = wpa_default_rsn_cipher(freq); ++ if (!data.has_group) ++ data.group_cipher = wpa_default_rsn_cipher(freq); + ++ if (wpa_key_mgmt_ft(data.key_mgmt) && !mdie && ++ !wpa_key_mgmt_only_ft(data.key_mgmt)) { ++ /* Workaround for some HP and Epson printers that seem ++ * to incorrectly copy the FT-PSK + WPA-PSK AKMs from AP ++ * advertised RSNE to Association Request frame. */ ++ wpa_printf(MSG_DEBUG, ++ "RSN: FT set in RSNE AKM but MDE is missing from " ++ MACSTR ++ " - ignore FT AKM(s) because there's also a non-FT AKM", ++ MAC2STR(sm->addr)); ++ data.key_mgmt &= ~WPA_KEY_MGMT_FT; ++ } ++ + selector = RSN_AUTH_KEY_MGMT_UNSPEC_802_1X; + if (0) { + } +@@ -509,12 +585,28 @@ + selector = RSN_AUTH_KEY_MGMT_802_1X_SUITE_B_192; + else if (data.key_mgmt & WPA_KEY_MGMT_IEEE8021X_SUITE_B) + selector = RSN_AUTH_KEY_MGMT_802_1X_SUITE_B; +-#ifdef CONFIG_IEEE80211R ++#ifdef CONFIG_FILS ++#ifdef CONFIG_IEEE80211R_AP ++ else if (data.key_mgmt & WPA_KEY_MGMT_FT_FILS_SHA384) ++ selector = RSN_AUTH_KEY_MGMT_FT_FILS_SHA384; ++ else if (data.key_mgmt & WPA_KEY_MGMT_FT_FILS_SHA256) ++ selector = RSN_AUTH_KEY_MGMT_FT_FILS_SHA256; ++#endif /* CONFIG_IEEE80211R_AP */ ++ else if (data.key_mgmt & WPA_KEY_MGMT_FILS_SHA384) ++ selector = RSN_AUTH_KEY_MGMT_FILS_SHA384; ++ else if (data.key_mgmt & WPA_KEY_MGMT_FILS_SHA256) ++ selector = RSN_AUTH_KEY_MGMT_FILS_SHA256; ++#endif /* CONFIG_FILS */ ++#ifdef CONFIG_IEEE80211R_AP ++#ifdef CONFIG_SHA384 ++ else if (data.key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X_SHA384) ++ selector = RSN_AUTH_KEY_MGMT_FT_802_1X_SHA384; ++#endif /* CONFIG_SHA384 */ + else if (data.key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X) + selector = RSN_AUTH_KEY_MGMT_FT_802_1X; + else if (data.key_mgmt & WPA_KEY_MGMT_FT_PSK) + selector = RSN_AUTH_KEY_MGMT_FT_PSK; +-#endif /* CONFIG_IEEE80211R */ ++#endif /* CONFIG_IEEE80211R_AP */ + #ifdef CONFIG_IEEE80211W + else if (data.key_mgmt & WPA_KEY_MGMT_IEEE8021X_SHA256) + selector = RSN_AUTH_KEY_MGMT_802_1X_SHA256; +@@ -531,6 +623,18 @@ + selector = RSN_AUTH_KEY_MGMT_UNSPEC_802_1X; + else if (data.key_mgmt & WPA_KEY_MGMT_PSK) + selector = RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X; ++#ifdef CONFIG_OWE ++ else if (data.key_mgmt & WPA_KEY_MGMT_OWE) ++ selector = RSN_AUTH_KEY_MGMT_OWE; ++#endif /* CONFIG_OWE */ ++#ifdef CONFIG_DPP ++ else if (data.key_mgmt & WPA_KEY_MGMT_DPP) ++ selector = RSN_AUTH_KEY_MGMT_DPP; ++#endif /* CONFIG_DPP */ ++#ifdef CONFIG_HS20 ++ else if (data.key_mgmt & WPA_KEY_MGMT_OSEN) ++ selector = RSN_AUTH_KEY_MGMT_OSEN; ++#endif /* CONFIG_HS20 */ + wpa_auth->dot11RSNAAuthenticationSuiteSelected = selector; + + selector = wpa_cipher_to_suite(WPA_PROTO_RSN, +@@ -591,12 +695,28 @@ + sm->wpa_key_mgmt = WPA_KEY_MGMT_IEEE8021X_SUITE_B_192; + else if (key_mgmt & WPA_KEY_MGMT_IEEE8021X_SUITE_B) + sm->wpa_key_mgmt = WPA_KEY_MGMT_IEEE8021X_SUITE_B; +-#ifdef CONFIG_IEEE80211R ++#ifdef CONFIG_FILS ++#ifdef CONFIG_IEEE80211R_AP ++ else if (key_mgmt & WPA_KEY_MGMT_FT_FILS_SHA384) ++ sm->wpa_key_mgmt = WPA_KEY_MGMT_FT_FILS_SHA384; ++ else if (data.key_mgmt & WPA_KEY_MGMT_FT_FILS_SHA256) ++ sm->wpa_key_mgmt = WPA_KEY_MGMT_FT_FILS_SHA256; ++#endif /* CONFIG_IEEE80211R_AP */ ++ else if (key_mgmt & WPA_KEY_MGMT_FILS_SHA384) ++ sm->wpa_key_mgmt = WPA_KEY_MGMT_FILS_SHA384; ++ else if (key_mgmt & WPA_KEY_MGMT_FILS_SHA256) ++ sm->wpa_key_mgmt = WPA_KEY_MGMT_FILS_SHA256; ++#endif /* CONFIG_FILS */ ++#ifdef CONFIG_IEEE80211R_AP ++#ifdef CONFIG_SHA384 ++ else if (key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X_SHA384) ++ sm->wpa_key_mgmt = WPA_KEY_MGMT_FT_IEEE8021X_SHA384; ++#endif /* CONFIG_SHA384 */ + else if (key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X) + sm->wpa_key_mgmt = WPA_KEY_MGMT_FT_IEEE8021X; + else if (key_mgmt & WPA_KEY_MGMT_FT_PSK) + sm->wpa_key_mgmt = WPA_KEY_MGMT_FT_PSK; +-#endif /* CONFIG_IEEE80211R */ ++#endif /* CONFIG_IEEE80211R_AP */ + #ifdef CONFIG_IEEE80211W + else if (key_mgmt & WPA_KEY_MGMT_IEEE8021X_SHA256) + sm->wpa_key_mgmt = WPA_KEY_MGMT_IEEE8021X_SHA256; +@@ -611,6 +731,18 @@ + #endif /* CONFIG_SAE */ + else if (key_mgmt & WPA_KEY_MGMT_IEEE8021X) + sm->wpa_key_mgmt = WPA_KEY_MGMT_IEEE8021X; ++#ifdef CONFIG_OWE ++ else if (key_mgmt & WPA_KEY_MGMT_OWE) ++ sm->wpa_key_mgmt = WPA_KEY_MGMT_OWE; ++#endif /* CONFIG_OWE */ ++#ifdef CONFIG_DPP ++ else if (key_mgmt & WPA_KEY_MGMT_DPP) ++ sm->wpa_key_mgmt = WPA_KEY_MGMT_DPP; ++#endif /* CONFIG_DPP */ ++#ifdef CONFIG_HS20 ++ else if (key_mgmt & WPA_KEY_MGMT_OSEN) ++ sm->wpa_key_mgmt = WPA_KEY_MGMT_OSEN; ++#endif /* CONFIG_HS20 */ + else + sm->wpa_key_mgmt = WPA_KEY_MGMT_PSK; + +@@ -634,12 +766,6 @@ + return WPA_MGMT_FRAME_PROTECTION_VIOLATION; + } + +- if (ciphers & WPA_CIPHER_TKIP) { +- wpa_printf(MSG_DEBUG, "Management frame protection " +- "cannot use TKIP"); +- return WPA_MGMT_FRAME_PROTECTION_VIOLATION; +- } +- + if (data.mgmt_group_cipher != wpa_auth->conf.group_mgmt_cipher) + { + wpa_printf(MSG_DEBUG, "Unsupported management group " +@@ -648,14 +774,42 @@ + } + } + ++#ifdef CONFIG_SAE ++ if (wpa_auth->conf.ieee80211w == MGMT_FRAME_PROTECTION_OPTIONAL && ++ wpa_auth->conf.sae_require_mfp && ++ wpa_key_mgmt_sae(sm->wpa_key_mgmt) && ++ !(data.capabilities & WPA_CAPABILITY_MFPC)) { ++ wpa_printf(MSG_DEBUG, ++ "Management frame protection required with SAE, but client did not enable it"); ++ return WPA_MGMT_FRAME_PROTECTION_VIOLATION; ++ } ++#endif /* CONFIG_SAE */ ++ ++#ifdef CONFIG_OCV ++ if ((data.capabilities & WPA_CAPABILITY_OCVC) && ++ !(data.capabilities & WPA_CAPABILITY_MFPC)) { ++ wpa_printf(MSG_DEBUG, ++ "Management frame protection required with OCV, but client did not enable it"); ++ return WPA_MGMT_FRAME_PROTECTION_VIOLATION; ++ } ++ wpa_auth_set_ocv(sm, wpa_auth->conf.ocv && ++ (data.capabilities & WPA_CAPABILITY_OCVC)); ++#endif /* CONFIG_OCV */ ++ + if (wpa_auth->conf.ieee80211w == NO_MGMT_FRAME_PROTECTION || + !(data.capabilities & WPA_CAPABILITY_MFPC)) + sm->mgmt_frame_prot = 0; + else + sm->mgmt_frame_prot = 1; ++ ++ if (sm->mgmt_frame_prot && (ciphers & WPA_CIPHER_TKIP)) { ++ wpa_printf(MSG_DEBUG, ++ "Management frame protection cannot use TKIP"); ++ return WPA_MGMT_FRAME_PROTECTION_VIOLATION; ++ } + #endif /* CONFIG_IEEE80211W */ + +-#ifdef CONFIG_IEEE80211R ++#ifdef CONFIG_IEEE80211R_AP + if (wpa_key_mgmt_ft(sm->wpa_key_mgmt)) { + if (mdie == NULL || mdie_len < MOBILITY_DOMAIN_ID_LEN + 1) { + wpa_printf(MSG_DEBUG, "RSN: Trying to use FT, but " +@@ -668,9 +822,32 @@ + "MDIE", mdie, MOBILITY_DOMAIN_ID_LEN); + return WPA_INVALID_MDIE; + } ++ } else if (mdie != NULL) { ++ wpa_printf(MSG_DEBUG, ++ "RSN: Trying to use non-FT AKM suite, but MDIE included"); ++ return WPA_INVALID_AKMP; + } +-#endif /* CONFIG_IEEE80211R */ ++#endif /* CONFIG_IEEE80211R_AP */ + ++#ifdef CONFIG_OWE ++ if (sm->wpa_key_mgmt == WPA_KEY_MGMT_OWE && !owe_dh) { ++ wpa_printf(MSG_DEBUG, ++ "OWE: No Diffie-Hellman Parameter element"); ++ return WPA_INVALID_AKMP; ++ } ++#ifdef CONFIG_DPP ++ if (sm->wpa_key_mgmt == WPA_KEY_MGMT_DPP && owe_dh) { ++ /* Diffie-Hellman Parameter element can be used with DPP as ++ * well, so allow this to proceed. */ ++ } else ++#endif /* CONFIG_DPP */ ++ if (sm->wpa_key_mgmt != WPA_KEY_MGMT_OWE && owe_dh) { ++ wpa_printf(MSG_DEBUG, ++ "OWE: Unexpected Diffie-Hellman Parameter element with non-OWE AKM"); ++ return WPA_INVALID_AKMP; ++ } ++#endif /* CONFIG_OWE */ ++ + sm->pairwise = wpa_pick_pairwise_cipher(ciphers, 0); + if (sm->pairwise < 0) + return WPA_INVALID_PAIRWISE; +@@ -681,6 +858,21 @@ + else + sm->wpa = WPA_VERSION_WPA; + ++#if defined(CONFIG_IEEE80211R_AP) && defined(CONFIG_FILS) ++ if ((sm->wpa_key_mgmt == WPA_KEY_MGMT_FT_FILS_SHA256 || ++ sm->wpa_key_mgmt == WPA_KEY_MGMT_FT_FILS_SHA384) && ++ (sm->auth_alg == WLAN_AUTH_FILS_SK || ++ sm->auth_alg == WLAN_AUTH_FILS_SK_PFS || ++ sm->auth_alg == WLAN_AUTH_FILS_PK) && ++ (data.num_pmkid != 1 || !data.pmkid || !sm->pmk_r1_name_valid || ++ os_memcmp_const(data.pmkid, sm->pmk_r1_name, ++ WPA_PMK_NAME_LEN) != 0)) { ++ wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_DEBUG, ++ "No PMKR1Name match for FILS+FT"); ++ return WPA_INVALID_PMKID; ++ } ++#endif /* CONFIG_IEEE80211R_AP && CONFIG_FILS */ ++ + sm->pmksa = NULL; + for (i = 0; i < data.num_pmkid; i++) { + wpa_hexdump(MSG_DEBUG, "RSN IE: STA PMKID", +@@ -712,14 +904,34 @@ + } + } + if (sm->pmksa && pmkid) { ++ struct vlan_description *vlan; ++ ++ vlan = sm->pmksa->vlan_desc; + wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_DEBUG, +- "PMKID found from PMKSA cache " +- "eap_type=%d vlan_id=%d", ++ "PMKID found from PMKSA cache eap_type=%d vlan=%d%s", + sm->pmksa->eap_type_authsrv, +- sm->pmksa->vlan_id); ++ vlan ? vlan->untagged : 0, ++ (vlan && vlan->tagged[0]) ? "+" : ""); + os_memcpy(wpa_auth->dot11RSNAPMKIDUsed, pmkid, PMKID_LEN); + } + ++#ifdef CONFIG_SAE ++ if (sm->wpa_key_mgmt == WPA_KEY_MGMT_SAE && data.num_pmkid && ++ !sm->pmksa) { ++ wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_DEBUG, ++ "No PMKSA cache entry found for SAE"); ++ return WPA_INVALID_PMKID; ++ } ++#endif /* CONFIG_SAE */ ++ ++#ifdef CONFIG_DPP ++ if (sm->wpa_key_mgmt == WPA_KEY_MGMT_DPP && !sm->pmksa) { ++ wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_DEBUG, ++ "No PMKSA cache entry found for DPP"); ++ return WPA_INVALID_PMKID; ++ } ++#endif /* CONFIG_DPP */ ++ + if (sm->wpa_ie == NULL || sm->wpa_ie_len < wpa_ie_len) { + os_free(sm->wpa_ie); + sm->wpa_ie = os_malloc(wpa_ie_len); +@@ -791,7 +1003,7 @@ + return 0; + } + +- if (pos + 1 + RSN_SELECTOR_LEN < end && ++ if (1 + RSN_SELECTOR_LEN < end - pos && + pos[1] >= RSN_SELECTOR_LEN + PMKID_LEN && + RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_PMKID) { + ie->pmkid = pos + 2 + RSN_SELECTOR_LEN; +@@ -812,36 +1024,6 @@ + return 0; + } + +-#ifdef CONFIG_PEERKEY +- if (pos[1] > RSN_SELECTOR_LEN + 2 && +- RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_SMK) { +- ie->smk = pos + 2 + RSN_SELECTOR_LEN; +- ie->smk_len = pos[1] - RSN_SELECTOR_LEN; +- return 0; +- } +- +- if (pos[1] > RSN_SELECTOR_LEN + 2 && +- RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_NONCE) { +- ie->nonce = pos + 2 + RSN_SELECTOR_LEN; +- ie->nonce_len = pos[1] - RSN_SELECTOR_LEN; +- return 0; +- } +- +- if (pos[1] > RSN_SELECTOR_LEN + 2 && +- RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_LIFETIME) { +- ie->lifetime = pos + 2 + RSN_SELECTOR_LEN; +- ie->lifetime_len = pos[1] - RSN_SELECTOR_LEN; +- return 0; +- } +- +- if (pos[1] > RSN_SELECTOR_LEN + 2 && +- RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_ERROR) { +- ie->error = pos + 2 + RSN_SELECTOR_LEN; +- ie->error_len = pos[1] - RSN_SELECTOR_LEN; +- return 0; +- } +-#endif /* CONFIG_PEERKEY */ +- + #ifdef CONFIG_IEEE80211W + if (pos[1] > RSN_SELECTOR_LEN + 2 && + RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_IGTK) { +@@ -870,6 +1052,15 @@ + } + #endif /* CONFIG_P2P */ + ++#ifdef CONFIG_OCV ++ if (pos[1] > RSN_SELECTOR_LEN + 2 && ++ RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_OCI) { ++ ie->oci = pos + 2 + RSN_SELECTOR_LEN; ++ ie->oci_len = pos[1] - RSN_SELECTOR_LEN; ++ return 0; ++ } ++#endif /* CONFIG_OCV */ ++ + return 0; + } + +@@ -887,13 +1078,13 @@ + int ret = 0; + + os_memset(ie, 0, sizeof(*ie)); +- for (pos = buf, end = pos + len; pos + 1 < end; pos += 2 + pos[1]) { ++ for (pos = buf, end = pos + len; end - pos > 1; pos += 2 + pos[1]) { + if (pos[0] == 0xdd && + ((pos == buf + len - 1) || pos[1] == 0)) { + /* Ignore padding */ + break; + } +- if (pos + 2 + pos[1] > end) { ++ if (2 + pos[1] > end - pos) { + wpa_printf(MSG_DEBUG, "WPA: EAPOL-Key Key Data " + "underflow (ie=%d len=%d pos=%d)", + pos[0], pos[1], (int) (pos - buf)); +@@ -905,7 +1096,7 @@ + if (*pos == WLAN_EID_RSN) { + ie->rsn_ie = pos; + ie->rsn_ie_len = pos[1] + 2; +-#ifdef CONFIG_IEEE80211R ++#ifdef CONFIG_IEEE80211R_AP + } else if (*pos == WLAN_EID_MOBILITY_DOMAIN) { + ie->mdie = pos; + ie->mdie_len = pos[1] + 2; +@@ -912,7 +1103,7 @@ + } else if (*pos == WLAN_EID_FAST_BSS_TRANSITION) { + ie->ftie = pos; + ie->ftie_len = pos[1] + 2; +-#endif /* CONFIG_IEEE80211R */ ++#endif /* CONFIG_IEEE80211R_AP */ + } else if (*pos == WLAN_EID_VENDOR_SPECIFIC) { + ret = wpa_parse_generic(pos, end, ie); + if (ret < 0) +@@ -935,3 +1126,53 @@ + { + return sm ? sm->mgmt_frame_prot : 0; + } ++ ++ ++#ifdef CONFIG_OCV ++ ++void wpa_auth_set_ocv(struct wpa_state_machine *sm, int ocv) ++{ ++ if (sm) ++ sm->ocv_enabled = ocv; ++} ++ ++ ++int wpa_auth_uses_ocv(struct wpa_state_machine *sm) ++{ ++ return sm ? sm->ocv_enabled : 0; ++} ++ ++#endif /* CONFIG_OCV */ ++ ++ ++#ifdef CONFIG_OWE ++u8 * wpa_auth_write_assoc_resp_owe(struct wpa_state_machine *sm, ++ u8 *pos, size_t max_len, ++ const u8 *req_ies, size_t req_ies_len) ++{ ++ int res; ++ struct wpa_auth_config *conf; ++ ++ if (!sm) ++ return pos; ++ conf = &sm->wpa_auth->conf; ++ ++#ifdef CONFIG_TESTING_OPTIONS ++ if (conf->own_ie_override_len) { ++ if (max_len < conf->own_ie_override_len) ++ return NULL; ++ wpa_hexdump(MSG_DEBUG, "WPA: Forced own IE(s) for testing", ++ conf->own_ie_override, conf->own_ie_override_len); ++ os_memcpy(pos, conf->own_ie_override, ++ conf->own_ie_override_len); ++ return pos + conf->own_ie_override_len; ++ } ++#endif /* CONFIG_TESTING_OPTIONS */ ++ ++ res = wpa_write_rsn_ie(conf, pos, max_len, ++ sm->pmksa ? sm->pmksa->pmkid : NULL); ++ if (res < 0) ++ return pos; ++ return pos + res; ++} ++#endif /* CONFIG_OWE */ +--- contrib/wpa/src/ap/wpa_auth_ie.h.orig ++++ contrib/wpa/src/ap/wpa_auth_ie.h +@@ -19,30 +19,24 @@ + size_t gtk_len; + const u8 *mac_addr; + size_t mac_addr_len; +-#ifdef CONFIG_PEERKEY +- const u8 *smk; +- size_t smk_len; +- const u8 *nonce; +- size_t nonce_len; +- const u8 *lifetime; +- size_t lifetime_len; +- const u8 *error; +- size_t error_len; +-#endif /* CONFIG_PEERKEY */ + #ifdef CONFIG_IEEE80211W + const u8 *igtk; + size_t igtk_len; + #endif /* CONFIG_IEEE80211W */ +-#ifdef CONFIG_IEEE80211R ++#ifdef CONFIG_IEEE80211R_AP + const u8 *mdie; + size_t mdie_len; + const u8 *ftie; + size_t ftie_len; +-#endif /* CONFIG_IEEE80211R */ ++#endif /* CONFIG_IEEE80211R_AP */ + #ifdef CONFIG_P2P + const u8 *ip_addr_req; + const u8 *ip_addr_alloc; + #endif /* CONFIG_P2P */ ++#ifdef CONFIG_OCV ++ const u8 *oci; ++ size_t oci_len; ++#endif /* CONFIG_OCV */ + + const u8 *osen; + size_t osen_len; +--- contrib/wpa/src/ap/wps_hostapd.c.orig ++++ contrib/wpa/src/ap/wps_hostapd.c +@@ -1,6 +1,6 @@ + /* + * hostapd / WPS integration +- * Copyright (c) 2008-2012, Jouni Malinen ++ * Copyright (c) 2008-2016, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. +@@ -269,12 +269,6 @@ + } + + +-static int str_starts(const char *str, const char *start) +-{ +- return os_strncmp(str, start, os_strlen(start)) == 0; +-} +- +- + static void wps_reload_config(void *eloop_data, void *user_ctx) + { + struct hostapd_iface *iface = eloop_data; +@@ -360,6 +354,18 @@ + bss->wpa_pairwise, + bss->rsn_pairwise); + ++ if (hapd->conf->wps_cred_add_sae && ++ (cred->auth_type & WPS_AUTH_WPA2PSK) && ++ cred->key_len != 2 * PMK_LEN) { ++ bss->wpa_key_mgmt |= WPA_KEY_MGMT_SAE; ++#ifdef CONFIG_IEEE80211W ++ if (bss->ieee80211w == NO_MGMT_FRAME_PROTECTION) ++ bss->ieee80211w = ++ MGMT_FRAME_PROTECTION_OPTIONAL; ++ bss->sae_require_mfp = 1; ++#endif /* CONFIG_IEEE80211W */ ++ } ++ + if (cred->key_len >= 8 && cred->key_len < 64) { + os_free(bss->ssid.wpa_passphrase); + bss->ssid.wpa_passphrase = os_zalloc(cred->key_len + 1); +@@ -407,6 +413,7 @@ + char buf[1024]; + int multi_bss; + int wpa; ++ int pmf_changed = 0; + + if (hapd->wps == NULL) + return 0; +@@ -445,6 +452,8 @@ + os_memcpy(hapd->wps->ssid, cred->ssid, cred->ssid_len); + hapd->wps->ssid_len = cred->ssid_len; + hapd->wps->encr_types = cred->encr_type; ++ hapd->wps->encr_types_rsn = cred->encr_type; ++ hapd->wps->encr_types_wpa = cred->encr_type; + hapd->wps->auth_types = cred->auth_type; + hapd->wps->ap_encr_type = cred->encr_type; + hapd->wps->ap_auth_type = cred->auth_type; +@@ -524,6 +533,10 @@ + + if (wpa) { + char *prefix; ++#ifdef CONFIG_IEEE80211W ++ int sae = 0; ++#endif /* CONFIG_IEEE80211W */ ++ + fprintf(nconf, "wpa=%d\n", wpa); + + fprintf(nconf, "wpa_key_mgmt="); +@@ -532,10 +545,30 @@ + fprintf(nconf, "WPA-EAP"); + prefix = " "; + } +- if (cred->auth_type & (WPS_AUTH_WPA2PSK | WPS_AUTH_WPAPSK)) ++ if (cred->auth_type & (WPS_AUTH_WPA2PSK | WPS_AUTH_WPAPSK)) { + fprintf(nconf, "%sWPA-PSK", prefix); ++ prefix = " "; ++ } ++ if (hapd->conf->wps_cred_add_sae && ++ (cred->auth_type & WPS_AUTH_WPA2PSK) && ++ cred->key_len != 2 * PMK_LEN) { ++ fprintf(nconf, "%sSAE", prefix); ++#ifdef CONFIG_IEEE80211W ++ sae = 1; ++#endif /* CONFIG_IEEE80211W */ ++ } + fprintf(nconf, "\n"); + ++#ifdef CONFIG_IEEE80211W ++ if (sae && hapd->conf->ieee80211w == NO_MGMT_FRAME_PROTECTION) { ++ fprintf(nconf, "ieee80211w=%d\n", ++ MGMT_FRAME_PROTECTION_OPTIONAL); ++ pmf_changed = 1; ++ } ++ if (sae) ++ fprintf(nconf, "sae_require_mfp=1\n"); ++#endif /* CONFIG_IEEE80211W */ ++ + fprintf(nconf, "wpa_pairwise="); + prefix = ""; + if (cred->encr_type & WPS_ENCR_AES) { +@@ -589,6 +622,7 @@ + str_starts(buf, "wep_default_key=") || + str_starts(buf, "wep_key") || + str_starts(buf, "wps_state=") || ++ (pmf_changed && str_starts(buf, "ieee80211w=")) || + str_starts(buf, "wpa=") || + str_starts(buf, "wpa_psk=") || + str_starts(buf, "wpa_pairwise=") || +@@ -872,7 +906,8 @@ + hapd->wps_probe_resp_ie = NULL; + + if (deinit_only) { +- hostapd_reset_ap_wps_ie(hapd); ++ if (hapd->drv_priv) ++ hostapd_reset_ap_wps_ie(hapd); + return; + } + +@@ -978,6 +1013,7 @@ + { + struct wps_context *wps; + struct wps_registrar_config cfg; ++ u8 *multi_ap_netw_key = NULL; + + if (conf->wps_state == 0) { + hostapd_wps_clear_ies(hapd, 0); +@@ -1067,10 +1103,16 @@ + if (conf->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X) + wps->auth_types |= WPS_AUTH_WPA2; + +- if (conf->rsn_pairwise & (WPA_CIPHER_CCMP | WPA_CIPHER_GCMP)) ++ if (conf->rsn_pairwise & (WPA_CIPHER_CCMP | WPA_CIPHER_GCMP | ++ WPA_CIPHER_CCMP_256 | ++ WPA_CIPHER_GCMP_256)) { + wps->encr_types |= WPS_ENCR_AES; +- if (conf->rsn_pairwise & WPA_CIPHER_TKIP) ++ wps->encr_types_rsn |= WPS_ENCR_AES; ++ } ++ if (conf->rsn_pairwise & WPA_CIPHER_TKIP) { + wps->encr_types |= WPS_ENCR_TKIP; ++ wps->encr_types_rsn |= WPS_ENCR_TKIP; ++ } + } + + if (conf->wpa & WPA_PROTO_WPA) { +@@ -1079,10 +1121,14 @@ + if (conf->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X) + wps->auth_types |= WPS_AUTH_WPA; + +- if (conf->wpa_pairwise & WPA_CIPHER_CCMP) ++ if (conf->wpa_pairwise & WPA_CIPHER_CCMP) { + wps->encr_types |= WPS_ENCR_AES; +- if (conf->wpa_pairwise & WPA_CIPHER_TKIP) ++ wps->encr_types_wpa |= WPS_ENCR_AES; ++ } ++ if (conf->wpa_pairwise & WPA_CIPHER_TKIP) { + wps->encr_types |= WPS_ENCR_TKIP; ++ wps->encr_types_wpa |= WPS_ENCR_TKIP; ++ } + } + + if (conf->ssid.security_policy == SECURITY_PLAINTEXT) { +@@ -1122,8 +1168,35 @@ + /* Override parameters to enable security by default */ + wps->auth_types = WPS_AUTH_WPA2PSK | WPS_AUTH_WPAPSK; + wps->encr_types = WPS_ENCR_AES | WPS_ENCR_TKIP; ++ wps->encr_types_rsn = WPS_ENCR_AES | WPS_ENCR_TKIP; ++ wps->encr_types_wpa = WPS_ENCR_AES | WPS_ENCR_TKIP; + } + ++ if ((hapd->conf->multi_ap & FRONTHAUL_BSS) && ++ hapd->conf->multi_ap_backhaul_ssid.ssid_len) { ++ cfg.multi_ap_backhaul_ssid_len = ++ hapd->conf->multi_ap_backhaul_ssid.ssid_len; ++ cfg.multi_ap_backhaul_ssid = ++ hapd->conf->multi_ap_backhaul_ssid.ssid; ++ ++ if (conf->multi_ap_backhaul_ssid.wpa_passphrase) { ++ cfg.multi_ap_backhaul_network_key = (const u8 *) ++ conf->multi_ap_backhaul_ssid.wpa_passphrase; ++ cfg.multi_ap_backhaul_network_key_len = ++ os_strlen(conf->multi_ap_backhaul_ssid.wpa_passphrase); ++ } else if (conf->multi_ap_backhaul_ssid.wpa_psk) { ++ multi_ap_netw_key = os_malloc(2 * PMK_LEN + 1); ++ if (!multi_ap_netw_key) ++ goto fail; ++ wpa_snprintf_hex((char *) multi_ap_netw_key, ++ 2 * PMK_LEN + 1, ++ conf->multi_ap_backhaul_ssid.wpa_psk->psk, ++ PMK_LEN); ++ cfg.multi_ap_backhaul_network_key = multi_ap_netw_key; ++ cfg.multi_ap_backhaul_network_key_len = 2 * PMK_LEN; ++ } ++ } ++ + wps->ap_settings = conf->ap_settings; + wps->ap_settings_len = conf->ap_settings_len; + +@@ -1165,10 +1238,12 @@ + hostapd_register_probereq_cb(hapd, hostapd_wps_probe_req_rx, hapd); + + hapd->wps = wps; ++ bin_clear_free(multi_ap_netw_key, 2 * PMK_LEN); + + return 0; + + fail: ++ bin_clear_free(multi_ap_netw_key, 2 * PMK_LEN); + hostapd_free_wps(wps); + return -1; + } +@@ -1614,7 +1689,8 @@ + unsigned int pin; + struct wps_ap_pin_data data; + +- pin = wps_generate_pin(); ++ if (wps_generate_pin(&pin) < 0) ++ return NULL; + os_snprintf(data.pin_txt, sizeof(data.pin_txt), "%08u", pin); + data.timeout = timeout; + hostapd_wps_for_each(hapd, wps_ap_pin_set, &data); +--- contrib/wpa/src/common/cli.c.orig ++++ contrib/wpa/src/common/cli.c +@@ -0,0 +1,267 @@ ++/* ++ * Common hostapd/wpa_supplicant command line interface functions ++ * Copyright (c) 2004-2016, Jouni Malinen ++ * ++ * This software may be distributed under the terms of the BSD license. ++ * See README for more details. ++ */ ++ ++#include "includes.h" ++ ++#include "utils/common.h" ++#include "common/cli.h" ++ ++ ++const char *const cli_license = ++"This software may be distributed under the terms of the BSD license.\n" ++"See README for more details.\n"; ++ ++const char *const cli_full_license = ++"This software may be distributed under the terms of the BSD license.\n" ++"\n" ++"Redistribution and use in source and binary forms, with or without\n" ++"modification, are permitted provided that the following conditions are\n" ++"met:\n" ++"\n" ++"1. Redistributions of source code must retain the above copyright\n" ++" notice, this list of conditions and the following disclaimer.\n" ++"\n" ++"2. Redistributions in binary form must reproduce the above copyright\n" ++" notice, this list of conditions and the following disclaimer in the\n" ++" documentation and/or other materials provided with the distribution.\n" ++"\n" ++"3. Neither the name(s) of the above-listed copyright holder(s) nor the\n" ++" names of its contributors may be used to endorse or promote products\n" ++" derived from this software without specific prior written permission.\n" ++"\n" ++"THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n" ++"\"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n" ++"LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n" ++"A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n" ++"OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n" ++"SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\n" ++"LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n" ++"DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\n" ++"THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n" ++"(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n" ++"OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n" ++"\n"; ++ ++ ++void cli_txt_list_free(struct cli_txt_entry *e) ++{ ++ dl_list_del(&e->list); ++ os_free(e->txt); ++ os_free(e); ++} ++ ++ ++void cli_txt_list_flush(struct dl_list *list) ++{ ++ struct cli_txt_entry *e; ++ ++ while ((e = dl_list_first(list, struct cli_txt_entry, list))) ++ cli_txt_list_free(e); ++} ++ ++ ++struct cli_txt_entry * cli_txt_list_get(struct dl_list *txt_list, ++ const char *txt) ++{ ++ struct cli_txt_entry *e; ++ ++ dl_list_for_each(e, txt_list, struct cli_txt_entry, list) { ++ if (os_strcmp(e->txt, txt) == 0) ++ return e; ++ } ++ return NULL; ++} ++ ++ ++void cli_txt_list_del(struct dl_list *txt_list, const char *txt) ++{ ++ struct cli_txt_entry *e; ++ ++ e = cli_txt_list_get(txt_list, txt); ++ if (e) ++ cli_txt_list_free(e); ++} ++ ++ ++void cli_txt_list_del_addr(struct dl_list *txt_list, const char *txt) ++{ ++ u8 addr[ETH_ALEN]; ++ char buf[18]; ++ ++ if (hwaddr_aton(txt, addr) < 0) ++ return; ++ os_snprintf(buf, sizeof(buf), MACSTR, MAC2STR(addr)); ++ cli_txt_list_del(txt_list, buf); ++} ++ ++ ++void cli_txt_list_del_word(struct dl_list *txt_list, const char *txt, ++ int separator) ++{ ++ const char *end; ++ char *buf; ++ ++ end = os_strchr(txt, separator); ++ if (end == NULL) ++ end = txt + os_strlen(txt); ++ buf = dup_binstr(txt, end - txt); ++ if (buf == NULL) ++ return; ++ cli_txt_list_del(txt_list, buf); ++ os_free(buf); ++} ++ ++ ++int cli_txt_list_add(struct dl_list *txt_list, const char *txt) ++{ ++ struct cli_txt_entry *e; ++ ++ e = cli_txt_list_get(txt_list, txt); ++ if (e) ++ return 0; ++ e = os_zalloc(sizeof(*e)); ++ if (e == NULL) ++ return -1; ++ e->txt = os_strdup(txt); ++ if (e->txt == NULL) { ++ os_free(e); ++ return -1; ++ } ++ dl_list_add(txt_list, &e->list); ++ return 0; ++} ++ ++ ++int cli_txt_list_add_addr(struct dl_list *txt_list, const char *txt) ++{ ++ u8 addr[ETH_ALEN]; ++ char buf[18]; ++ ++ if (hwaddr_aton(txt, addr) < 0) ++ return -1; ++ os_snprintf(buf, sizeof(buf), MACSTR, MAC2STR(addr)); ++ return cli_txt_list_add(txt_list, buf); ++} ++ ++ ++int cli_txt_list_add_word(struct dl_list *txt_list, const char *txt, ++ int separator) ++{ ++ const char *end; ++ char *buf; ++ int ret; ++ ++ end = os_strchr(txt, separator); ++ if (end == NULL) ++ end = txt + os_strlen(txt); ++ buf = dup_binstr(txt, end - txt); ++ if (buf == NULL) ++ return -1; ++ ret = cli_txt_list_add(txt_list, buf); ++ os_free(buf); ++ return ret; ++} ++ ++ ++char ** cli_txt_list_array(struct dl_list *txt_list) ++{ ++ unsigned int i, count = dl_list_len(txt_list); ++ char **res; ++ struct cli_txt_entry *e; ++ ++ res = os_calloc(count + 1, sizeof(char *)); ++ if (res == NULL) ++ return NULL; ++ ++ i = 0; ++ dl_list_for_each(e, txt_list, struct cli_txt_entry, list) { ++ res[i] = os_strdup(e->txt); ++ if (res[i] == NULL) ++ break; ++ i++; ++ } ++ ++ return res; ++} ++ ++ ++int get_cmd_arg_num(const char *str, int pos) ++{ ++ int arg = 0, i; ++ ++ for (i = 0; i <= pos; i++) { ++ if (str[i] != ' ') { ++ arg++; ++ while (i <= pos && str[i] != ' ') ++ i++; ++ } ++ } ++ ++ if (arg > 0) ++ arg--; ++ return arg; ++} ++ ++ ++int write_cmd(char *buf, size_t buflen, const char *cmd, int argc, char *argv[]) ++{ ++ int i, res; ++ char *pos, *end; ++ ++ pos = buf; ++ end = buf + buflen; ++ ++ res = os_snprintf(pos, end - pos, "%s", cmd); ++ if (os_snprintf_error(end - pos, res)) ++ goto fail; ++ pos += res; ++ ++ for (i = 0; i < argc; i++) { ++ res = os_snprintf(pos, end - pos, " %s", argv[i]); ++ if (os_snprintf_error(end - pos, res)) ++ goto fail; ++ pos += res; ++ } ++ ++ buf[buflen - 1] = '\0'; ++ return 0; ++ ++fail: ++ printf("Too long command\n"); ++ return -1; ++} ++ ++ ++int tokenize_cmd(char *cmd, char *argv[]) ++{ ++ char *pos; ++ int argc = 0; ++ ++ pos = cmd; ++ for (;;) { ++ while (*pos == ' ') ++ pos++; ++ if (*pos == '\0') ++ break; ++ argv[argc] = pos; ++ argc++; ++ if (argc == max_args) ++ break; ++ if (*pos == '"') { ++ char *pos2 = os_strrchr(pos, '"'); ++ if (pos2) ++ pos = pos2 + 1; ++ } ++ while (*pos != '\0' && *pos != ' ') ++ pos++; ++ if (*pos == ' ') ++ *pos++ = '\0'; ++ } ++ ++ return argc; ++} +--- contrib/wpa/src/common/cli.h.orig ++++ contrib/wpa/src/common/cli.h +@@ -0,0 +1,47 @@ ++/* ++ * Common hostapd/wpa_supplicant command line interface functionality ++ * Copyright (c) 2004-2016, Jouni Malinen ++ * ++ * This software may be distributed under the terms of the BSD license. ++ * See README for more details. ++ */ ++ ++#ifndef CLI_H ++#define CLI_H ++ ++#include "utils/list.h" ++ ++extern const char *const cli_license; ++extern const char *const cli_full_license; ++ ++struct cli_txt_entry { ++ struct dl_list list; ++ char *txt; ++}; ++ ++void cli_txt_list_free(struct cli_txt_entry *e); ++void cli_txt_list_flush(struct dl_list *list); ++ ++struct cli_txt_entry * ++cli_txt_list_get(struct dl_list *txt_list, const char *txt); ++ ++void cli_txt_list_del(struct dl_list *txt_list, const char *txt); ++void cli_txt_list_del_addr(struct dl_list *txt_list, const char *txt); ++void cli_txt_list_del_word(struct dl_list *txt_list, const char *txt, ++ int separator); ++ ++int cli_txt_list_add(struct dl_list *txt_list, const char *txt); ++int cli_txt_list_add_addr(struct dl_list *txt_list, const char *txt); ++int cli_txt_list_add_word(struct dl_list *txt_list, const char *txt, ++ int separator); ++ ++char ** cli_txt_list_array(struct dl_list *txt_list); ++ ++int get_cmd_arg_num(const char *str, int pos); ++int write_cmd(char *buf, size_t buflen, const char *cmd, int argc, ++ char *argv[]); ++ ++#define max_args 10 ++int tokenize_cmd(char *cmd, char *argv[]); ++ ++#endif /* CLI_H */ +--- contrib/wpa/src/common/common_module_tests.c.orig ++++ contrib/wpa/src/common/common_module_tests.c +@@ -1,6 +1,6 @@ + /* + * common module tests +- * Copyright (c) 2014-2015, Jouni Malinen ++ * Copyright (c) 2014-2019, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. +@@ -9,10 +9,13 @@ + #include "utils/includes.h" + + #include "utils/common.h" ++#include "utils/module_tests.h" ++#include "crypto/crypto.h" + #include "ieee802_11_common.h" + #include "ieee802_11_defs.h" + #include "gas.h" + #include "wpa_common.h" ++#include "sae.h" + + + struct ieee802_11_parse_test_data { +@@ -52,6 +55,31 @@ + 18, ParseOK, 9 }, + { (u8 *) "\x8b\x00", 2, ParseOK, 1 }, + { (u8 *) "\xdd\x04\x00\x90\x4c\x04", 6, ParseUnknown, 1 }, ++ { (u8 *) "\xed\x00", 2, ParseOK, 1 }, ++ { (u8 *) "\xef\x00", 2, ParseOK, 1 }, ++ { (u8 *) "\xef\x01\x11", 3, ParseOK, 1 }, ++ { (u8 *) "\xf0\x00", 2, ParseOK, 1 }, ++ { (u8 *) "\xf1\x00", 2, ParseOK, 1 }, ++ { (u8 *) "\xf1\x02\x11\x22", 4, ParseOK, 1 }, ++ { (u8 *) "\xf2\x00", 2, ParseOK, 1 }, ++ { (u8 *) "\xff\x00", 2, ParseUnknown, 1 }, ++ { (u8 *) "\xff\x01\x00", 3, ParseUnknown, 1 }, ++ { (u8 *) "\xff\x01\x01", 3, ParseOK, 1 }, ++ { (u8 *) "\xff\x02\x01\x00", 4, ParseOK, 1 }, ++ { (u8 *) "\xff\x01\x02", 3, ParseOK, 1 }, ++ { (u8 *) "\xff\x04\x02\x11\x22\x33", 6, ParseOK, 1 }, ++ { (u8 *) "\xff\x01\x04", 3, ParseOK, 1 }, ++ { (u8 *) "\xff\x01\x05", 3, ParseOK, 1 }, ++ { (u8 *) "\xff\x0d\x05\x11\x22\x33\x44\x55\x55\x11\x22\x33\x44\x55\x55", ++ 15, ParseOK, 1 }, ++ { (u8 *) "\xff\x01\x06", 3, ParseOK, 1 }, ++ { (u8 *) "\xff\x02\x06\x00", 4, ParseOK, 1 }, ++ { (u8 *) "\xff\x01\x07", 3, ParseOK, 1 }, ++ { (u8 *) "\xff\x09\x07\x11\x22\x33\x44\x55\x66\x77\x88", 11, ++ ParseOK, 1 }, ++ { (u8 *) "\xff\x01\x0c", 3, ParseOK, 1 }, ++ { (u8 *) "\xff\x02\x0c\x00", 4, ParseOK, 1 }, ++ { (u8 *) "\xff\x01\x0d", 3, ParseOK, 1 }, + { NULL, 0, ParseOK, 0 } + }; + +@@ -58,6 +86,7 @@ + static int ieee802_11_parse_tests(void) + { + int i, ret = 0; ++ struct wpabuf *buf; + + wpa_printf(MSG_INFO, "ieee802_11_parse tests"); + +@@ -83,6 +112,35 @@ + ret = -1; + } + ++ buf = ieee802_11_vendor_ie_concat((const u8 *) "\xdd\x05\x11\x22\x33\x44\x01\xdd\x05\x11\x22\x33\x44\x02\x00\x01", ++ 16, 0x11223344); ++ do { ++ const u8 *pos; ++ ++ if (!buf) { ++ wpa_printf(MSG_ERROR, ++ "ieee802_11_vendor_ie_concat test 2 failed"); ++ ret = -1; ++ break; ++ } ++ ++ if (wpabuf_len(buf) != 2) { ++ wpa_printf(MSG_ERROR, ++ "ieee802_11_vendor_ie_concat test 3 failed"); ++ ret = -1; ++ break; ++ } ++ ++ pos = wpabuf_head(buf); ++ if (pos[0] != 0x01 || pos[1] != 0x02) { ++ wpa_printf(MSG_ERROR, ++ "ieee802_11_vendor_ie_concat test 3 failed"); ++ ret = -1; ++ break; ++ } ++ } while (0); ++ wpabuf_free(buf); ++ + return ret; + } + +@@ -192,6 +250,179 @@ + } + + ++static int sae_tests(void) ++{ ++#ifdef CONFIG_SAE ++ struct sae_data sae; ++ int ret = -1; ++ /* IEEE P802.11-REVmd/D2.1, Annex J.10 */ ++ const u8 addr1[ETH_ALEN] = { 0x82, 0x7b, 0x91, 0x9d, 0xd4, 0xb9 }; ++ const u8 addr2[ETH_ALEN] = { 0x1e, 0xec, 0x49, 0xea, 0x64, 0x88 }; ++ const char *pw = "mekmitasdigoat"; ++ const char *pwid = "psk4internet"; ++ const u8 local_rand[] = { ++ 0xa9, 0x06, 0xf6, 0x1e, 0x4d, 0x3a, 0x5d, 0x4e, ++ 0xb2, 0x96, 0x5f, 0xf3, 0x4c, 0xf9, 0x17, 0xdd, ++ 0x04, 0x44, 0x45, 0xc8, 0x78, 0xc1, 0x7c, 0xa5, ++ 0xd5, 0xb9, 0x37, 0x86, 0xda, 0x9f, 0x83, 0xcf ++ }; ++ const u8 local_mask[] = { ++ 0x42, 0x34, 0xb4, 0xfb, 0x17, 0xaa, 0x43, 0x5c, ++ 0x52, 0xfb, 0xfd, 0xeb, 0xe6, 0x40, 0x39, 0xb4, ++ 0x34, 0x78, 0x20, 0x0e, 0x54, 0xff, 0x7b, 0x6e, ++ 0x07, 0xb6, 0x9c, 0xad, 0x74, 0x15, 0x3c, 0x15 ++ }; ++ const u8 local_commit[] = { ++ 0x13, 0x00, 0xeb, 0x3b, 0xab, 0x19, 0x64, 0xe4, ++ 0xa0, 0xab, 0x05, 0x92, 0x5d, 0xdf, 0x33, 0x39, ++ 0x51, 0x91, 0x38, 0xbc, 0x65, 0xd6, 0xcd, 0xc0, ++ 0xf8, 0x13, 0xdd, 0x6f, 0xd4, 0x34, 0x4e, 0xb4, ++ 0xbf, 0xe4, 0x4b, 0x5c, 0x21, 0x59, 0x76, 0x58, ++ 0xf4, 0xe3, 0xed, 0xdf, 0xb4, 0xb9, 0x9f, 0x25, ++ 0xb4, 0xd6, 0x54, 0x0f, 0x32, 0xff, 0x1f, 0xd5, ++ 0xc5, 0x30, 0xc6, 0x0a, 0x79, 0x44, 0x48, 0x61, ++ 0x0b, 0xc6, 0xde, 0x3d, 0x92, 0xbd, 0xbb, 0xd4, ++ 0x7d, 0x93, 0x59, 0x80, 0xca, 0x6c, 0xf8, 0x98, ++ 0x8a, 0xb6, 0x63, 0x0b, 0xe6, 0x76, 0x4c, 0x88, ++ 0x5c, 0xeb, 0x97, 0x93, 0x97, 0x0f, 0x69, 0x52, ++ 0x17, 0xee, 0xff, 0x0d, 0x21, 0x70, 0x73, 0x6b, ++ 0x34, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, ++ 0x74 ++ }; ++ const u8 peer_commit[] = { ++ 0x13, 0x00, 0x55, 0x64, 0xf0, 0x45, 0xb2, 0xea, ++ 0x1e, 0x56, 0x6c, 0xf1, 0xdd, 0x74, 0x1f, 0x70, ++ 0xd9, 0xbe, 0x35, 0xd2, 0xdf, 0x5b, 0x9a, 0x55, ++ 0x02, 0x94, 0x6e, 0xe0, 0x3c, 0xf8, 0xda, 0xe2, ++ 0x7e, 0x1e, 0x05, 0xb8, 0x43, 0x0e, 0xb7, 0xa9, ++ 0x9e, 0x24, 0x87, 0x7c, 0xe6, 0x9b, 0xaf, 0x3d, ++ 0xc5, 0x80, 0xe3, 0x09, 0x63, 0x3d, 0x6b, 0x38, ++ 0x5f, 0x83, 0xee, 0x1c, 0x3e, 0xc3, 0x59, 0x1f, ++ 0x1a, 0x53, 0x93, 0xc0, 0x6e, 0x80, 0x5d, 0xdc, ++ 0xeb, 0x2f, 0xde, 0x50, 0x93, 0x0d, 0xd7, 0xcf, ++ 0xeb, 0xb9, 0x87, 0xc6, 0xff, 0x96, 0x66, 0xaf, ++ 0x16, 0x4e, 0xb5, 0x18, 0x4d, 0x8e, 0x66, 0x62, ++ 0xed, 0x6a, 0xff, 0x0d, 0x21, 0x70, 0x73, 0x6b, ++ 0x34, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, ++ 0x74 ++ }; ++ const u8 kck[] = { ++ 0x59, 0x9d, 0x6f, 0x1e, 0x27, 0x54, 0x8b, 0xe8, ++ 0x49, 0x9d, 0xce, 0xed, 0x2f, 0xec, 0xcf, 0x94, ++ 0x81, 0x8c, 0xe1, 0xc7, 0x9f, 0x1b, 0x4e, 0xb3, ++ 0xd6, 0xa5, 0x32, 0x28, 0xa0, 0x9b, 0xf3, 0xed ++ }; ++ const u8 pmk[] = { ++ 0x7a, 0xea, 0xd8, 0x6f, 0xba, 0x4c, 0x32, 0x21, ++ 0xfc, 0x43, 0x7f, 0x5f, 0x14, 0xd7, 0x0d, 0x85, ++ 0x4e, 0xa5, 0xd5, 0xaa, 0xc1, 0x69, 0x01, 0x16, ++ 0x79, 0x30, 0x81, 0xed, 0xa4, 0xd5, 0x57, 0xc5 ++ }; ++ const u8 pmkid[] = { ++ 0x40, 0xa0, 0x9b, 0x60, 0x17, 0xce, 0xbf, 0x00, ++ 0x72, 0x84, 0x3b, 0x53, 0x52, 0xaa, 0x2b, 0x4f ++ }; ++ const u8 local_confirm[] = { ++ 0x01, 0x00, 0x12, 0xd9, 0xd5, 0xc7, 0x8c, 0x50, ++ 0x05, 0x26, 0xd3, 0x6c, 0x41, 0xdb, 0xc5, 0x6a, ++ 0xed, 0xf2, 0x91, 0x4c, 0xed, 0xdd, 0xd7, 0xca, ++ 0xd4, 0xa5, 0x8c, 0x48, 0xf8, 0x3d, 0xbd, 0xe9, ++ 0xfc, 0x77 ++ }; ++ const u8 peer_confirm[] = { ++ 0x01, 0x00, 0x02, 0x87, 0x1c, 0xf9, 0x06, 0x89, ++ 0x8b, 0x80, 0x60, 0xec, 0x18, 0x41, 0x43, 0xbe, ++ 0x77, 0xb8, 0xc0, 0x8a, 0x80, 0x19, 0xb1, 0x3e, ++ 0xb6, 0xd0, 0xae, 0xf0, 0xd8, 0x38, 0x3d, 0xfa, ++ 0xc2, 0xfd ++ }; ++ struct wpabuf *buf = NULL; ++ struct crypto_bignum *mask = NULL; ++ ++ os_memset(&sae, 0, sizeof(sae)); ++ buf = wpabuf_alloc(1000); ++ if (!buf || ++ sae_set_group(&sae, 19) < 0 || ++ sae_prepare_commit(addr1, addr2, (const u8 *) pw, os_strlen(pw), ++ pwid, &sae) < 0) ++ goto fail; ++ ++ /* Override local values based on SAE test vector */ ++ crypto_bignum_deinit(sae.tmp->sae_rand, 1); ++ sae.tmp->sae_rand = crypto_bignum_init_set(local_rand, ++ sizeof(local_rand)); ++ mask = crypto_bignum_init_set(local_mask, sizeof(local_mask)); ++ if (!sae.tmp->sae_rand || !mask) ++ goto fail; ++ ++ if (crypto_bignum_add(sae.tmp->sae_rand, mask, ++ sae.tmp->own_commit_scalar) < 0 || ++ crypto_bignum_mod(sae.tmp->own_commit_scalar, sae.tmp->order, ++ sae.tmp->own_commit_scalar) < 0 || ++ crypto_ec_point_mul(sae.tmp->ec, sae.tmp->pwe_ecc, mask, ++ sae.tmp->own_commit_element_ecc) < 0 || ++ crypto_ec_point_invert(sae.tmp->ec, ++ sae.tmp->own_commit_element_ecc) < 0) ++ goto fail; ++ ++ /* Check that output matches the test vector */ ++ sae_write_commit(&sae, buf, NULL, pwid); ++ wpa_hexdump_buf(MSG_DEBUG, "SAE: Commit message", buf); ++ ++ if (wpabuf_len(buf) != sizeof(local_commit) || ++ os_memcmp(wpabuf_head(buf), local_commit, ++ sizeof(local_commit)) != 0) { ++ wpa_printf(MSG_ERROR, "SAE: Mismatch in local commit"); ++ goto fail; ++ } ++ ++ if (sae_parse_commit(&sae, peer_commit, sizeof(peer_commit), NULL, NULL, ++ NULL) != 0 || ++ sae_process_commit(&sae) < 0) ++ goto fail; ++ ++ if (os_memcmp(kck, sae.tmp->kck, SAE_KCK_LEN) != 0) { ++ wpa_printf(MSG_ERROR, "SAE: Mismatch in KCK"); ++ goto fail; ++ } ++ ++ if (os_memcmp(pmk, sae.pmk, SAE_PMK_LEN) != 0) { ++ wpa_printf(MSG_ERROR, "SAE: Mismatch in PMK"); ++ goto fail; ++ } ++ ++ if (os_memcmp(pmkid, sae.pmkid, SAE_PMKID_LEN) != 0) { ++ wpa_printf(MSG_ERROR, "SAE: Mismatch in PMKID"); ++ goto fail; ++ } ++ ++ buf->used = 0; ++ sae.send_confirm = 1; ++ sae_write_confirm(&sae, buf); ++ wpa_hexdump_buf(MSG_DEBUG, "SAE: Confirm message", buf); ++ ++ if (wpabuf_len(buf) != sizeof(local_confirm) || ++ os_memcmp(wpabuf_head(buf), local_confirm, ++ sizeof(local_confirm)) != 0) { ++ wpa_printf(MSG_ERROR, "SAE: Mismatch in local confirm"); ++ goto fail; ++ } ++ ++ if (sae_check_confirm(&sae, peer_confirm, sizeof(peer_confirm)) < 0) ++ goto fail; ++ ++ ret = 0; ++fail: ++ sae_clear_data(&sae); ++ wpabuf_free(buf); ++ crypto_bignum_deinit(mask, 1); ++ return ret; ++#else /* CONFIG_SAE */ ++ return 0; ++#endif /* CONFIG_SAE */ ++} ++ ++ + int common_module_tests(void) + { + int ret = 0; +@@ -200,6 +431,7 @@ + + if (ieee802_11_parse_tests() < 0 || + gas_tests() < 0 || ++ sae_tests() < 0 || + rsn_ie_parse_tests() < 0) + ret = -1; + +--- contrib/wpa/src/common/ctrl_iface_common.c.orig ++++ contrib/wpa/src/common/ctrl_iface_common.c +@@ -0,0 +1,209 @@ ++/* ++ * Common hostapd/wpa_supplicant ctrl iface code. ++ * Copyright (c) 2002-2013, Jouni Malinen ++ * Copyright (c) 2015, 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 ++#include ++ ++#include "utils/common.h" ++#include "ctrl_iface_common.h" ++ ++static int sockaddr_compare(struct sockaddr_storage *a, socklen_t a_len, ++ struct sockaddr_storage *b, socklen_t b_len) ++{ ++ if (a->ss_family != b->ss_family) ++ return 1; ++ ++ switch (a->ss_family) { ++#ifdef CONFIG_CTRL_IFACE_UDP ++ case AF_INET: ++ { ++ struct sockaddr_in *in_a, *in_b; ++ ++ in_a = (struct sockaddr_in *) a; ++ in_b = (struct sockaddr_in *) b; ++ ++ if (in_a->sin_port != in_b->sin_port) ++ return 1; ++ if (in_a->sin_addr.s_addr != in_b->sin_addr.s_addr) ++ return 1; ++ break; ++ } ++ case AF_INET6: ++ { ++ struct sockaddr_in6 *in6_a, *in6_b; ++ ++ in6_a = (struct sockaddr_in6 *) a; ++ in6_b = (struct sockaddr_in6 *) b; ++ ++ if (in6_a->sin6_port != in6_b->sin6_port) ++ return 1; ++ if (os_memcmp(&in6_a->sin6_addr, &in6_b->sin6_addr, ++ sizeof(in6_a->sin6_addr)) != 0) ++ return 1; ++ break; ++ } ++#endif /* CONFIG_CTRL_IFACE_UDP */ ++#ifdef CONFIG_CTRL_IFACE_UNIX ++ case AF_UNIX: ++ { ++ struct sockaddr_un *u_a, *u_b; ++ ++ u_a = (struct sockaddr_un *) a; ++ u_b = (struct sockaddr_un *) b; ++ ++ if (a_len != b_len || ++ os_memcmp(u_a->sun_path, u_b->sun_path, ++ a_len - offsetof(struct sockaddr_un, sun_path)) ++ != 0) ++ return 1; ++ break; ++ } ++#endif /* CONFIG_CTRL_IFACE_UNIX */ ++ default: ++ return 1; ++ } ++ ++ return 0; ++} ++ ++ ++void sockaddr_print(int level, const char *msg, struct sockaddr_storage *sock, ++ socklen_t socklen) ++{ ++ switch (sock->ss_family) { ++#ifdef CONFIG_CTRL_IFACE_UDP ++ case AF_INET: ++ case AF_INET6: ++ { ++ char host[NI_MAXHOST] = { 0 }; ++ char service[NI_MAXSERV] = { 0 }; ++ ++ getnameinfo((struct sockaddr *) sock, socklen, ++ host, sizeof(host), ++ service, sizeof(service), ++ NI_NUMERICHOST); ++ ++ wpa_printf(level, "%s %s:%s", msg, host, service); ++ break; ++ } ++#endif /* CONFIG_CTRL_IFACE_UDP */ ++#ifdef CONFIG_CTRL_IFACE_UNIX ++ case AF_UNIX: ++ { ++ char addr_txt[200]; ++ ++ printf_encode(addr_txt, sizeof(addr_txt), ++ (u8 *) ((struct sockaddr_un *) sock)->sun_path, ++ socklen - offsetof(struct sockaddr_un, sun_path)); ++ wpa_printf(level, "%s %s", msg, addr_txt); ++ break; ++ } ++#endif /* CONFIG_CTRL_IFACE_UNIX */ ++ default: ++ wpa_printf(level, "%s", msg); ++ break; ++ } ++} ++ ++ ++static int ctrl_set_events(struct wpa_ctrl_dst *dst, const char *input) ++{ ++ const char *value; ++ int val; ++ ++ if (!input) ++ return 0; ++ ++ value = os_strchr(input, '='); ++ if (!value) ++ return -1; ++ value++; ++ val = atoi(value); ++ if (val < 0 || val > 1) ++ return -1; ++ ++ if (str_starts(input, "probe_rx_events=")) { ++ if (val) ++ dst->events |= WPA_EVENT_RX_PROBE_REQUEST; ++ else ++ dst->events &= ~WPA_EVENT_RX_PROBE_REQUEST; ++ } ++ ++ return 0; ++} ++ ++ ++int ctrl_iface_attach(struct dl_list *ctrl_dst, struct sockaddr_storage *from, ++ socklen_t fromlen, const char *input) ++{ ++ struct wpa_ctrl_dst *dst; ++ ++ /* Update event registration if already attached */ ++ dl_list_for_each(dst, ctrl_dst, struct wpa_ctrl_dst, list) { ++ if (!sockaddr_compare(from, fromlen, ++ &dst->addr, dst->addrlen)) ++ return ctrl_set_events(dst, input); ++ } ++ ++ /* New attachment */ ++ dst = os_zalloc(sizeof(*dst)); ++ if (dst == NULL) ++ return -1; ++ os_memcpy(&dst->addr, from, fromlen); ++ dst->addrlen = fromlen; ++ dst->debug_level = MSG_INFO; ++ ctrl_set_events(dst, input); ++ dl_list_add(ctrl_dst, &dst->list); ++ ++ sockaddr_print(MSG_DEBUG, "CTRL_IFACE monitor attached", from, fromlen); ++ return 0; ++} ++ ++ ++int ctrl_iface_detach(struct dl_list *ctrl_dst, struct sockaddr_storage *from, ++ socklen_t fromlen) ++{ ++ struct wpa_ctrl_dst *dst; ++ ++ dl_list_for_each(dst, ctrl_dst, struct wpa_ctrl_dst, list) { ++ if (!sockaddr_compare(from, fromlen, ++ &dst->addr, dst->addrlen)) { ++ sockaddr_print(MSG_DEBUG, "CTRL_IFACE monitor detached", ++ from, fromlen); ++ dl_list_del(&dst->list); ++ os_free(dst); ++ return 0; ++ } ++ } ++ ++ return -1; ++} ++ ++ ++int ctrl_iface_level(struct dl_list *ctrl_dst, struct sockaddr_storage *from, ++ socklen_t fromlen, const char *level) ++{ ++ struct wpa_ctrl_dst *dst; ++ ++ wpa_printf(MSG_DEBUG, "CTRL_IFACE LEVEL %s", level); ++ ++ dl_list_for_each(dst, ctrl_dst, struct wpa_ctrl_dst, list) { ++ if (!sockaddr_compare(from, fromlen, ++ &dst->addr, dst->addrlen)) { ++ sockaddr_print(MSG_DEBUG, ++ "CTRL_IFACE changed monitor level", ++ from, fromlen); ++ dst->debug_level = atoi(level); ++ return 0; ++ } ++ } ++ ++ return -1; ++} +--- contrib/wpa/src/common/ctrl_iface_common.h.orig ++++ contrib/wpa/src/common/ctrl_iface_common.h +@@ -0,0 +1,42 @@ ++/* ++ * Common hostapd/wpa_supplicant ctrl iface code. ++ * Copyright (c) 2002-2013, Jouni Malinen ++ * Copyright (c) 2015, Qualcomm Atheros, Inc. ++ * ++ * This software may be distributed under the terms of the BSD license. ++ * See README for more details. ++ */ ++#ifndef CONTROL_IFACE_COMMON_H ++#define CONTROL_IFACE_COMMON_H ++ ++#include "utils/list.h" ++ ++/* Events enable bits (wpa_ctrl_dst::events) */ ++#define WPA_EVENT_RX_PROBE_REQUEST BIT(0) ++ ++/** ++ * struct wpa_ctrl_dst - Data structure of control interface monitors ++ * ++ * This structure is used to store information about registered control ++ * interface monitors into struct wpa_supplicant. ++ */ ++struct wpa_ctrl_dst { ++ struct dl_list list; ++ struct sockaddr_storage addr; ++ socklen_t addrlen; ++ int debug_level; ++ int errors; ++ u32 events; /* WPA_EVENT_* bitmap */ ++}; ++ ++void sockaddr_print(int level, const char *msg, struct sockaddr_storage *sock, ++ socklen_t socklen); ++ ++int ctrl_iface_attach(struct dl_list *ctrl_dst, struct sockaddr_storage *from, ++ socklen_t fromlen, const char *input); ++int ctrl_iface_detach(struct dl_list *ctrl_dst, struct sockaddr_storage *from, ++ socklen_t fromlen); ++int ctrl_iface_level(struct dl_list *ctrl_dst, struct sockaddr_storage *from, ++ socklen_t fromlen, const char *level); ++ ++#endif /* CONTROL_IFACE_COMMON_H */ +--- contrib/wpa/src/common/defs.h.orig ++++ contrib/wpa/src/common/defs.h +@@ -1,6 +1,6 @@ + /* + * WPA Supplicant - Common definitions +- * Copyright (c) 2004-2015, Jouni Malinen ++ * Copyright (c) 2004-2018, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. +@@ -51,16 +51,35 @@ + #define WPA_KEY_MGMT_OSEN BIT(15) + #define WPA_KEY_MGMT_IEEE8021X_SUITE_B BIT(16) + #define WPA_KEY_MGMT_IEEE8021X_SUITE_B_192 BIT(17) ++#define WPA_KEY_MGMT_FILS_SHA256 BIT(18) ++#define WPA_KEY_MGMT_FILS_SHA384 BIT(19) ++#define WPA_KEY_MGMT_FT_FILS_SHA256 BIT(20) ++#define WPA_KEY_MGMT_FT_FILS_SHA384 BIT(21) ++#define WPA_KEY_MGMT_OWE BIT(22) ++#define WPA_KEY_MGMT_DPP BIT(23) ++#define WPA_KEY_MGMT_FT_IEEE8021X_SHA384 BIT(24) + ++#define WPA_KEY_MGMT_FT (WPA_KEY_MGMT_FT_PSK | \ ++ WPA_KEY_MGMT_FT_IEEE8021X | \ ++ WPA_KEY_MGMT_FT_IEEE8021X_SHA384 | \ ++ WPA_KEY_MGMT_FT_SAE | \ ++ WPA_KEY_MGMT_FT_FILS_SHA256 | \ ++ WPA_KEY_MGMT_FT_FILS_SHA384) ++ + static inline int wpa_key_mgmt_wpa_ieee8021x(int akm) + { + return !!(akm & (WPA_KEY_MGMT_IEEE8021X | + WPA_KEY_MGMT_FT_IEEE8021X | ++ WPA_KEY_MGMT_FT_IEEE8021X_SHA384 | + WPA_KEY_MGMT_CCKM | + WPA_KEY_MGMT_OSEN | + WPA_KEY_MGMT_IEEE8021X_SHA256 | + WPA_KEY_MGMT_IEEE8021X_SUITE_B | +- WPA_KEY_MGMT_IEEE8021X_SUITE_B_192)); ++ WPA_KEY_MGMT_IEEE8021X_SUITE_B_192 | ++ WPA_KEY_MGMT_FILS_SHA256 | ++ WPA_KEY_MGMT_FILS_SHA384 | ++ WPA_KEY_MGMT_FT_FILS_SHA256 | ++ WPA_KEY_MGMT_FT_FILS_SHA384)); + } + + static inline int wpa_key_mgmt_wpa_psk(int akm) +@@ -74,11 +93,21 @@ + + static inline int wpa_key_mgmt_ft(int akm) + { +- return !!(akm & (WPA_KEY_MGMT_FT_PSK | +- WPA_KEY_MGMT_FT_IEEE8021X | +- WPA_KEY_MGMT_FT_SAE)); ++ return !!(akm & WPA_KEY_MGMT_FT); + } + ++static inline int wpa_key_mgmt_only_ft(int akm) ++{ ++ int ft = wpa_key_mgmt_ft(akm); ++ akm &= ~WPA_KEY_MGMT_FT; ++ return ft && !akm; ++} ++ ++static inline int wpa_key_mgmt_ft_psk(int akm) ++{ ++ return !!(akm & WPA_KEY_MGMT_FT_PSK); ++} ++ + static inline int wpa_key_mgmt_sae(int akm) + { + return !!(akm & (WPA_KEY_MGMT_SAE | +@@ -85,17 +114,32 @@ + WPA_KEY_MGMT_FT_SAE)); + } + ++static inline int wpa_key_mgmt_fils(int akm) ++{ ++ return !!(akm & (WPA_KEY_MGMT_FILS_SHA256 | ++ WPA_KEY_MGMT_FILS_SHA384 | ++ WPA_KEY_MGMT_FT_FILS_SHA256 | ++ WPA_KEY_MGMT_FT_FILS_SHA384)); ++} ++ + static inline int wpa_key_mgmt_sha256(int akm) + { + return !!(akm & (WPA_KEY_MGMT_PSK_SHA256 | + WPA_KEY_MGMT_IEEE8021X_SHA256 | ++ WPA_KEY_MGMT_SAE | ++ WPA_KEY_MGMT_FT_SAE | + WPA_KEY_MGMT_OSEN | +- WPA_KEY_MGMT_IEEE8021X_SUITE_B)); ++ WPA_KEY_MGMT_IEEE8021X_SUITE_B | ++ WPA_KEY_MGMT_FILS_SHA256 | ++ WPA_KEY_MGMT_FT_FILS_SHA256)); + } + + static inline int wpa_key_mgmt_sha384(int akm) + { +- return !!(akm & WPA_KEY_MGMT_IEEE8021X_SUITE_B_192); ++ return !!(akm & (WPA_KEY_MGMT_IEEE8021X_SUITE_B_192 | ++ WPA_KEY_MGMT_FT_IEEE8021X_SHA384 | ++ WPA_KEY_MGMT_FILS_SHA384 | ++ WPA_KEY_MGMT_FT_FILS_SHA384)); + } + + static inline int wpa_key_mgmt_suite_b(int akm) +@@ -108,7 +152,10 @@ + { + return wpa_key_mgmt_wpa_ieee8021x(akm) || + wpa_key_mgmt_wpa_psk(akm) || +- wpa_key_mgmt_sae(akm); ++ wpa_key_mgmt_fils(akm) || ++ wpa_key_mgmt_sae(akm) || ++ akm == WPA_KEY_MGMT_OWE || ++ akm == WPA_KEY_MGMT_DPP; + } + + static inline int wpa_key_mgmt_wpa_any(int akm) +@@ -132,7 +179,13 @@ + #define WPA_AUTH_ALG_LEAP BIT(2) + #define WPA_AUTH_ALG_FT BIT(3) + #define WPA_AUTH_ALG_SAE BIT(4) ++#define WPA_AUTH_ALG_FILS BIT(5) ++#define WPA_AUTH_ALG_FILS_SK_PFS BIT(6) + ++static inline int wpa_auth_alg_fils(int alg) ++{ ++ return !!(alg & (WPA_AUTH_ALG_FILS | WPA_AUTH_ALG_FILS_SK_PFS)); ++} + + enum wpa_alg { + WPA_ALG_NONE, +@@ -312,6 +365,7 @@ + WPA_CTRL_REQ_EAP_PASSPHRASE, + WPA_CTRL_REQ_SIM, + WPA_CTRL_REQ_PSK_PASSPHRASE, ++ WPA_CTRL_REQ_EXT_CERT_CHECK, + NUM_WPA_CTRL_REQS + }; + +@@ -319,13 +373,13 @@ + #define EAP_MAX_METHODS 8 + + enum mesh_plink_state { +- PLINK_LISTEN = 1, +- PLINK_OPEN_SENT, +- PLINK_OPEN_RCVD, ++ PLINK_IDLE = 1, ++ PLINK_OPN_SNT, ++ PLINK_OPN_RCVD, + PLINK_CNF_RCVD, + PLINK_ESTAB, + PLINK_HOLDING, +- PLINK_BLOCKED, ++ PLINK_BLOCKED, /* not defined in the IEEE 802.11 standard */ + }; + + enum set_band { +@@ -334,4 +388,35 @@ + WPA_SETBAND_2G + }; + ++enum wpa_radio_work_band { ++ BAND_2_4_GHZ = BIT(0), ++ BAND_5_GHZ = BIT(1), ++ BAND_60_GHZ = BIT(2), ++}; ++ ++enum beacon_rate_type { ++ BEACON_RATE_LEGACY, ++ BEACON_RATE_HT, ++ BEACON_RATE_VHT ++}; ++ ++enum eap_proxy_sim_state { ++ SIM_STATE_ERROR, ++}; ++ ++#define OCE_STA BIT(0) ++#define OCE_STA_CFON BIT(1) ++#define OCE_AP BIT(2) ++ ++/* enum chan_width - Channel width definitions */ ++enum chan_width { ++ CHAN_WIDTH_20_NOHT, ++ CHAN_WIDTH_20, ++ CHAN_WIDTH_40, ++ CHAN_WIDTH_80, ++ CHAN_WIDTH_80P80, ++ CHAN_WIDTH_160, ++ CHAN_WIDTH_UNKNOWN ++}; ++ + #endif /* DEFS_H */ +--- contrib/wpa/src/common/dhcp.h.orig ++++ contrib/wpa/src/common/dhcp.h +@@ -0,0 +1,279 @@ ++/* ++ * DHCP definitions ++ * Copyright (c) 2014-2017, Qualcomm Atheros, Inc. ++ * ++ * This software may be distributed under the terms of the BSD license. ++ * See README for more details. ++ */ ++ ++#ifndef DHCP_H ++#define DHCP_H ++ ++/* ++ * Translate Linux to FreeBSD ++ */ ++#define iphdr ip ++#define ihl ip_hl ++#define verson ip_v ++#define tos ip_tos ++#define tot_len ip_len ++#define id ip_id ++#define frag_off ip_off ++#define ttl ip_ttl ++#define protocol ip_p ++#define check ip_sum ++#define saddr ip_src ++#define daddr ip_dst ++ ++#include ++#if __FAVOR_BSD ++#include ++#else ++#define __FAVOR_BSD 1 ++#include ++#undef __FAVOR_BSD ++#endif ++ ++#define DHCP_SERVER_PORT 67 ++#define DHCP_CLIENT_PORT 68 ++ ++struct dhcp_data { ++ u8 op; ++ u8 htype; ++ u8 hlen; ++ u8 hops; ++ be32 xid; ++ be16 secs; ++ be16 flags; ++ be32 client_ip; ++ be32 your_ip; ++ be32 server_ip; ++ be32 relay_ip; ++ u8 hw_addr[16]; ++ u8 serv_name[64]; ++ u8 boot_file[128]; ++} STRUCT_PACKED; ++ ++struct bootp_pkt { ++ struct iphdr iph; ++ struct udphdr udph; ++ u8 op; ++ u8 htype; ++ u8 hlen; ++ u8 hops; ++ be32 xid; ++ be16 secs; ++ be16 flags; ++ be32 client_ip; ++ be32 your_ip; ++ be32 server_ip; ++ be32 relay_ip; ++ u8 hw_addr[16]; ++ u8 serv_name[64]; ++ u8 boot_file[128]; ++ u8 exten[312]; ++} STRUCT_PACKED; ++ ++#define DHCP_MAGIC 0x63825363 ++ ++/* ++ * IANA DHCP/BOOTP registry ++ * http://www.iana.org/assignments/bootp-dhcp-parameters/bootp-dhcp-parameters.xhtml ++*/ ++enum dhcp_options { ++ DHCP_OPT_PAD = 0, ++ DHCP_OPT_SUBNET_MASK = 1, ++ DHCP_OPT_TIME_OFFSET = 2, ++ DHCP_OPT_ROUTER = 3, ++ DHCP_OPT_TIME_SERVER = 4, ++ DHCP_OPT_NAME_SERVER = 5, ++ DHCP_OPT_DOMAIN_NAME_SERVER = 6, ++ DHCP_OPT_LOG_SERVER = 7, ++ DHCP_OPT_QUOTES_SERVER = 8, ++ DHCP_OPT_LPR_SERVER = 9, ++ DHCP_OPT_IMPRESS_SERVER = 10, ++ DHCP_OPT_RLP_SERVER = 11, ++ DHCP_OPT_HOSTNAME = 12, ++ DHCP_OPT_BOOT_FILE_SIZE = 13, ++ DHCP_OPT_MERIT_DUMP_FILE = 14, ++ DHCP_OPT_DOMAIN_NAME = 15, ++ DHCP_OPT_SWAP_SERVER = 16, ++ DHCP_OPT_ROOT_PATH = 17, ++ DHCP_OPT_EXTENSION_PATH = 18, ++ DHCP_OPT_FORWARD = 19, ++ DHCP_OPT_SRC_RTE = 20, ++ DHCP_OPT_POLICY_FILTER = 21, ++ DHCP_OPT_MAX_DG_ASSEMBLY = 22, ++ DHCP_OPT_DEFAULT_IP_TTL = 23, ++ DHCP_OPT_MTU_TIMEOUT = 24, ++ DHCP_OPT_MTU_PLATEAU = 25, ++ DHCP_OPT_MTU_INTERFACE = 26, ++ DHCP_OPT_ALL_SUBNETS_LOCAL = 27, ++ DHCP_OPT_BROADCAST_ADDRESS = 28, ++ DHCP_OPT_MASK_DISCOVERY = 29, ++ DHCP_OPT_MASK_SUPPLIER = 30, ++ DHCP_OPT_ROUTER_DISCOVERY = 31, ++ DHCP_OPT_ROUTER_SOLICITATION_ADDRESS = 32, ++ DHCP_OPT_STATIC_ROUTE = 33, ++ DHCP_OPT_TRAILERS = 34, ++ DHCP_OPT_ARP_TIMEOUT = 35, ++ DHCP_OPT_ETHERNET = 36, ++ DHCP_OPT_TCP_DEFAULT_TTL = 37, ++ DHCP_OPT_TCP_KEEPALIVE_INTERVAL = 38, ++ DHCP_OPT_TCP_KEEPALIVE_GARBAGE = 39, ++ DHCP_OPT_NIS_DOMAIN = 40, ++ DHCP_OPT_NIS_SERVERS = 41, ++ DHCP_OPT_NTP_SERVERS = 42, ++ DHCP_OPT_VENDOR_SPECIFIC = 43, ++ DHCP_OPT_NETBIOS_NAME_SERVER = 44, ++ DHCP_OPT_NETBIOS_DISTRIBUTION_SERVER = 45, ++ DHCP_OPT_NETBIOS_NODE_TYPE = 46, ++ DHCP_OPT_NETBIOS_SCOPE = 47, ++ DHCP_OPT_FONT_SERVER = 48, ++ DHCP_OPT_DISPLAY_MANAGER = 49, ++ DHCP_OPT_REQUESTED_IP_ADDRESS = 50, ++ DHCP_OPT_IP_ADDRESS_LEASE_TIME = 51, ++ DHCP_OPT_OVERLOAD = 52, ++ DHCP_OPT_MSG_TYPE = 53, ++ DHCP_OPT_SERVER_ID = 54, ++ DHCP_OPT_PARAMETER_REQ_LIST = 55, ++ DHCP_OPT_MESSAGE = 56, ++ DHCP_OPT_MAX_MESSAGE_SIZE = 57, ++ DHCP_OPT_RENEWAL_TIME = 58, ++ DHCP_OPT_REBINDING_TIME = 59, ++ DHCP_OPT_VENDOR_CLASS_ID = 60, ++ DHCP_OPT_CLIENT_ID = 61, ++ DHCP_OPT_NETWARE_IP_DOMAIN = 62, ++ DHCP_OPT_NETWARE_IP_OPTION = 63, ++ DHCP_OPT_NIS_V3_DOMAIN = 64, ++ DHCP_OPT_NIS_V3_SERVERS = 65, ++ DHCP_OPT_TFTP_SERVER_NAME = 66, ++ DHCP_OPT_BOOT_FILE_NAME = 67, ++ DHCP_OPT_HOME_AGENT_ADDRESSES = 68, ++ DHCP_OPT_SMTP_SERVER = 69, ++ DHCP_OPT_POP3_SERVER = 70, ++ DHCP_OPT_NNTP_SERVER = 71, ++ DHCP_OPT_WWW_SERVER = 72, ++ DHCP_OPT_FINGER_SERVER = 73, ++ DHCP_OPT_IRC_SERVER = 74, ++ DHCP_OPT_STREETTALK_SERVER = 75, ++ DHCP_OPT_STDA_SERVER = 76, ++ DHCP_OPT_USER_CLASS = 77, ++ DHCP_OPT_DIRECTORY_AGENT = 78, ++ DHCP_OPT_SERVICE_SCOPE = 79, ++ DHCP_OPT_RAPID_COMMIT = 80, ++ DHCP_OPT_CLIENT_FQDN = 81, ++ DHCP_OPT_RELAY_AGENT_INFO = 82, ++ DHCP_OPT_ISNS = 83, ++ DHCP_OPT_NDS_SERVERS = 85, ++ DHCP_OPT_NDS_TREE_NAME = 86, ++ DHCP_OPT_NDS_CONTEXT = 87, ++ DHCP_OPT_BCMCS_CONTROLLER_DOMAIN_NAME_LIST = 88, ++ DHCP_OPT_BCMCS_CONTROLLER_IPV4_ADDRESS = 89, ++ DHCP_OPT_AUTHENTICATION = 90, ++ DHCP_OPT_CLIENT_LAST_TRANSACTION_TIME = 91, ++ DHCP_OPT_ASSOCIATED_IP = 92, ++ DHCP_OPT_CLIENT_SYSYEM = 93, ++ DHCP_OPT_CLIENT_NDI = 94, ++ DHCP_OPT_LDAP = 95, ++ DHCP_OPT_UUID_GUID = 97, ++ DHCP_OPT_USER_AUTH = 98, ++ DHCP_OPT_GEOCONF_CIVIC = 99, ++ DHCP_OPT_PCODE = 100, ++ DHCP_OPT_TCODE = 101, ++ DHCP_OPT_NETINFO_ADDRESS = 112, ++ DHCP_OPT_NETINFO_TAG = 113, ++ DHCP_OPT_URL = 114, ++ DHCP_OPT_AUTO_CONFIG = 116, ++ DHCP_OPT_NAME_SERVICE_SEARCH = 117, ++ DHCP_OPT_SUBNET_SELECTION = 118, ++ DHCP_OPT_DOMAIN_SEARCH = 119, ++ DHCP_OPT_SIP_SERVERS_DCP = 120, ++ DHCP_OPT_CLASSLESS_STATIC_ROUTE = 121, ++ DHCP_OPT_CCC = 122, ++ DHCP_OPT_GEOCONF = 123, ++ DHCP_OPT_V_I_VENDOR_CLASS = 124, ++ DHCP_OPT_V_I_VENDOR_SPECIFIC_INFO = 125, ++ DHCP_OPT_PANA_AGENT = 136, ++ DHCP_OPT_V4_LOST = 137, ++ DHCP_OPT_CAPWAP_AC_V4 = 138, ++ DHCP_OPT_IPV4_ADDRESS_MOS = 139, ++ DHCP_OPT_IPV4_FQDN_MOS = 140, ++ DHCP_OPT_SIP_UA_CONF = 141, ++ DHCP_OPT_IPV4_ADDRESS_ANDSF = 142, ++ DHCP_OPT_GEOLOC = 144, ++ DHCP_OPT_FORCERENEW_NONCE_CAPABLE = 145, ++ DHCP_OPT_RDNSS_SELECTION = 146, ++ DHCP_OPT_TFTP_SERVER_ADDRESS = 150, ++ DHCP_OPT_STATUS_CODE = 151, ++ DHCP_OPT_BASE_TIME = 152, ++ DHCP_OPT_START_TIME_OF_STATE = 153, ++ DHCP_OPT_QUERY_START_TIME = 154, ++ DHCP_OPT_QUERY_END_TIME = 155, ++ DHCP_OPT_STATE = 156, ++ DHCP_OPT_DATA_SOURCE = 157, ++ DHCP_OPT_V4_PCP_SERVER = 158, ++ DHCP_OPT_V4_PORTPARAMS = 159, ++ DHCP_OPT_CAPTIVE_PORTAL = 160, ++ DHCP_OPT_CONF_FILE = 209, ++ DHCP_OPT_PATH_PREFIX = 210, ++ DHCP_OPT_REBOOT_TIME = 211, ++ DHCP_OPT_6RD = 212, ++ DHCP_OPT_V4_ACCESS_DOMAIN = 213, ++ DHCP_OPT_SUBNET_ALLOCATION = 220, ++ DHCP_OPT_VSS = 221, ++ DHCP_OPT_END = 255 ++}; ++ ++enum dhcp_message_types { ++ DHCPDISCOVER = 1, ++ DHCPOFFER = 2, ++ DHCPREQUEST = 3, ++ DHCPDECLINE = 4, ++ DHCPACK = 5, ++ DHCPNAK = 6, ++ DHCPRELEASE = 7, ++ DHCPINFORM = 8, ++ DHCPFORCERENEW = 9, ++ DHCPLEASEQUERY = 10, ++ DHCPLEASEUNASSIGNED = 11, ++ DHCPLEASEUNKNOWN = 12, ++ DHCPLEASEACTIVE = 13, ++ DHCPBULKLEASEQUERY = 14, ++ DHCPLEASEQUERYDONE = 15, ++ DHCPACTIVELEASEQUERY = 16, ++ DHCPLEASEQUERYSTATUS = 17, ++ DHCPTLS = 18, ++}; ++ ++enum dhcp_relay_agent_suboptions { ++ DHCP_RELAY_OPT_AGENT_CIRCUIT_ID = 1, ++ DHCP_RELAY_OPT_AGENT_REMOTE_ID = 2, ++ DHCP_RELAY_OPT_DOCSIS_DEVICE_CLASS = 4, ++ DHCP_RELAY_OPT_LINK_SELECTION = 5, ++ DHCP_RELAY_OPT_SUBSCRIBE_ID = 6, ++ DHCP_RELAY_OPT_RADIUS_ATTRIBUTES = 7, ++ DHCP_RELAY_OPT_AUTHENTICATION = 8, ++ DHCP_RELAY_OPT_VEDOR_SPECIFIC = 9, ++ DHCP_RELAY_OPT_RELAY_AGENT_FLAGS = 10, ++ DHCP_RELAY_OPT_SERVER_ID_OVERRIDE = 11, ++ DHCP_RELAY_OPT_RELAY_AGENT_ID = 12, ++ DHCP_RELAY_OPT_ACCESS_TECHNOLOGY_TYPE = 13, ++ DHCP_RELAY_OPT_ACCESS_NETWORK_NAME = 14, ++ DHCP_RELAY_OPT_ACCESS_POINT_NAME = 15, ++ DHCP_RELAY_OPT_ACCESS_POINT_BSSID = 16, ++ DHCP_RELAY_OPT_OPERATOR_ID = 17, ++ DHCP_RELAY_OPT_OPERATOR_REALM = 18, ++ DHCP_RELAY_OPT_DHCPV4_VIRTUAL_SUBNET_SELECTION = 151, ++ DHCP_RELAY_OPT_DHCPV4_VIRTUAL_SUBNET_SELECTION_CONTROL = 152, ++}; ++ ++enum access_technology_types { ++ ACCESS_TECHNOLOGY_VIRTUAL = 1, ++ ACCESS_TECHNOLOGY_PPP = 2, ++ ACCESS_TECHNOLOGY_ETHERNET = 3, ++ ACCESS_TECHNOLOGY_WLAN = 4, ++ ACCESS_TECHNOLOGY_WIMAX = 5, ++}; ++ ++#endif /* DHCP_H */ +--- contrib/wpa/src/common/dpp.c.orig ++++ contrib/wpa/src/common/dpp.c +@@ -0,0 +1,8721 @@ ++/* ++ * DPP functionality shared between hostapd and wpa_supplicant ++ * Copyright (c) 2017, Qualcomm Atheros, Inc. ++ * Copyright (c) 2018-2019, The Linux Foundation ++ * ++ * This software may be distributed under the terms of the BSD license. ++ * See README for more details. ++ */ ++ ++#include "utils/includes.h" ++#include ++#include ++#include ++#include ++ ++#include "utils/common.h" ++#include "utils/base64.h" ++#include "utils/json.h" ++#include "common/ieee802_11_common.h" ++#include "common/ieee802_11_defs.h" ++#include "common/wpa_ctrl.h" ++#include "common/gas.h" ++#include "crypto/crypto.h" ++#include "crypto/random.h" ++#include "crypto/aes.h" ++#include "crypto/aes_siv.h" ++#include "crypto/sha384.h" ++#include "crypto/sha512.h" ++#include "drivers/driver.h" ++#include "dpp.h" ++ ++ ++#ifdef CONFIG_TESTING_OPTIONS ++enum dpp_test_behavior dpp_test = DPP_TEST_DISABLED; ++u8 dpp_pkex_own_mac_override[ETH_ALEN] = { 0, 0, 0, 0, 0, 0 }; ++u8 dpp_pkex_peer_mac_override[ETH_ALEN] = { 0, 0, 0, 0, 0, 0 }; ++u8 dpp_pkex_ephemeral_key_override[600]; ++size_t dpp_pkex_ephemeral_key_override_len = 0; ++u8 dpp_protocol_key_override[600]; ++size_t dpp_protocol_key_override_len = 0; ++u8 dpp_nonce_override[DPP_MAX_NONCE_LEN]; ++size_t dpp_nonce_override_len = 0; ++ ++static int dpp_test_gen_invalid_key(struct wpabuf *msg, ++ const struct dpp_curve_params *curve); ++#endif /* CONFIG_TESTING_OPTIONS */ ++ ++#if OPENSSL_VERSION_NUMBER < 0x10100000L || \ ++ (defined(LIBRESSL_VERSION_NUMBER) && \ ++ LIBRESSL_VERSION_NUMBER < 0x20700000L) ++/* Compatibility wrappers for older versions. */ ++ ++static int ECDSA_SIG_set0(ECDSA_SIG *sig, BIGNUM *r, BIGNUM *s) ++{ ++ sig->r = r; ++ sig->s = s; ++ return 1; ++} ++ ++ ++static void ECDSA_SIG_get0(const ECDSA_SIG *sig, const BIGNUM **pr, ++ const BIGNUM **ps) ++{ ++ if (pr) ++ *pr = sig->r; ++ if (ps) ++ *ps = sig->s; ++} ++ ++#endif ++ ++ ++struct dpp_global { ++ struct dl_list bootstrap; /* struct dpp_bootstrap_info */ ++ struct dl_list configurator; /* struct dpp_configurator */ ++}; ++ ++static const struct dpp_curve_params dpp_curves[] = { ++ /* The mandatory to support and the default NIST P-256 curve needs to ++ * be the first entry on this list. */ ++ { "prime256v1", 32, 32, 16, 32, "P-256", 19, "ES256" }, ++ { "secp384r1", 48, 48, 24, 48, "P-384", 20, "ES384" }, ++ { "secp521r1", 64, 64, 32, 66, "P-521", 21, "ES512" }, ++ { "brainpoolP256r1", 32, 32, 16, 32, "BP-256", 28, "BS256" }, ++ { "brainpoolP384r1", 48, 48, 24, 48, "BP-384", 29, "BS384" }, ++ { "brainpoolP512r1", 64, 64, 32, 64, "BP-512", 30, "BS512" }, ++ { NULL, 0, 0, 0, 0, NULL, 0, NULL } ++}; ++ ++ ++/* Role-specific elements for PKEX */ ++ ++/* NIST P-256 */ ++static const u8 pkex_init_x_p256[32] = { ++ 0x56, 0x26, 0x12, 0xcf, 0x36, 0x48, 0xfe, 0x0b, ++ 0x07, 0x04, 0xbb, 0x12, 0x22, 0x50, 0xb2, 0x54, ++ 0xb1, 0x94, 0x64, 0x7e, 0x54, 0xce, 0x08, 0x07, ++ 0x2e, 0xec, 0xca, 0x74, 0x5b, 0x61, 0x2d, 0x25 ++ }; ++static const u8 pkex_init_y_p256[32] = { ++ 0x3e, 0x44, 0xc7, 0xc9, 0x8c, 0x1c, 0xa1, 0x0b, ++ 0x20, 0x09, 0x93, 0xb2, 0xfd, 0xe5, 0x69, 0xdc, ++ 0x75, 0xbc, 0xad, 0x33, 0xc1, 0xe7, 0xc6, 0x45, ++ 0x4d, 0x10, 0x1e, 0x6a, 0x3d, 0x84, 0x3c, 0xa4 ++ }; ++static const u8 pkex_resp_x_p256[32] = { ++ 0x1e, 0xa4, 0x8a, 0xb1, 0xa4, 0xe8, 0x42, 0x39, ++ 0xad, 0x73, 0x07, 0xf2, 0x34, 0xdf, 0x57, 0x4f, ++ 0xc0, 0x9d, 0x54, 0xbe, 0x36, 0x1b, 0x31, 0x0f, ++ 0x59, 0x91, 0x52, 0x33, 0xac, 0x19, 0x9d, 0x76 ++}; ++static const u8 pkex_resp_y_p256[32] = { ++ 0xd9, 0xfb, 0xf6, 0xb9, 0xf5, 0xfa, 0xdf, 0x19, ++ 0x58, 0xd8, 0x3e, 0xc9, 0x89, 0x7a, 0x35, 0xc1, ++ 0xbd, 0xe9, 0x0b, 0x77, 0x7a, 0xcb, 0x91, 0x2a, ++ 0xe8, 0x21, 0x3f, 0x47, 0x52, 0x02, 0x4d, 0x67 ++}; ++ ++/* NIST P-384 */ ++static const u8 pkex_init_x_p384[48] = { ++ 0x95, 0x3f, 0x42, 0x9e, 0x50, 0x7f, 0xf9, 0xaa, ++ 0xac, 0x1a, 0xf2, 0x85, 0x2e, 0x64, 0x91, 0x68, ++ 0x64, 0xc4, 0x3c, 0xb7, 0x5c, 0xf8, 0xc9, 0x53, ++ 0x6e, 0x58, 0x4c, 0x7f, 0xc4, 0x64, 0x61, 0xac, ++ 0x51, 0x8a, 0x6f, 0xfe, 0xab, 0x74, 0xe6, 0x12, ++ 0x81, 0xac, 0x38, 0x5d, 0x41, 0xe6, 0xb9, 0xa3 ++}; ++static const u8 pkex_init_y_p384[48] = { ++ 0x76, 0x2f, 0x68, 0x84, 0xa6, 0xb0, 0x59, 0x29, ++ 0x83, 0xa2, 0x6c, 0xa4, 0x6c, 0x3b, 0xf8, 0x56, ++ 0x76, 0x11, 0x2a, 0x32, 0x90, 0xbd, 0x07, 0xc7, ++ 0x37, 0x39, 0x9d, 0xdb, 0x96, 0xf3, 0x2b, 0xb6, ++ 0x27, 0xbb, 0x29, 0x3c, 0x17, 0x33, 0x9d, 0x94, ++ 0xc3, 0xda, 0xac, 0x46, 0xb0, 0x8e, 0x07, 0x18 ++}; ++static const u8 pkex_resp_x_p384[48] = { ++ 0xad, 0xbe, 0xd7, 0x1d, 0x3a, 0x71, 0x64, 0x98, ++ 0x5f, 0xb4, 0xd6, 0x4b, 0x50, 0xd0, 0x84, 0x97, ++ 0x4b, 0x7e, 0x57, 0x70, 0xd2, 0xd9, 0xf4, 0x92, ++ 0x2a, 0x3f, 0xce, 0x99, 0xc5, 0x77, 0x33, 0x44, ++ 0x14, 0x56, 0x92, 0xcb, 0xae, 0x46, 0x64, 0xdf, ++ 0xe0, 0xbb, 0xd7, 0xb1, 0x29, 0x20, 0x72, 0xdf ++}; ++static const u8 pkex_resp_y_p384[48] = { ++ 0xab, 0xa7, 0xdf, 0x52, 0xaa, 0xe2, 0x35, 0x0c, ++ 0xe3, 0x75, 0x32, 0xe6, 0xbf, 0x06, 0xc8, 0x7c, ++ 0x38, 0x29, 0x4c, 0xec, 0x82, 0xac, 0xd7, 0xa3, ++ 0x09, 0xd2, 0x0e, 0x22, 0x5a, 0x74, 0x52, 0xa1, ++ 0x7e, 0x54, 0x4e, 0xfe, 0xc6, 0x29, 0x33, 0x63, ++ 0x15, 0xe1, 0x7b, 0xe3, 0x40, 0x1c, 0xca, 0x06 ++}; ++ ++/* NIST P-521 */ ++static const u8 pkex_init_x_p521[66] = { ++ 0x00, 0x16, 0x20, 0x45, 0x19, 0x50, 0x95, 0x23, ++ 0x0d, 0x24, 0xbe, 0x00, 0x87, 0xdc, 0xfa, 0xf0, ++ 0x58, 0x9a, 0x01, 0x60, 0x07, 0x7a, 0xca, 0x76, ++ 0x01, 0xab, 0x2d, 0x5a, 0x46, 0xcd, 0x2c, 0xb5, ++ 0x11, 0x9a, 0xff, 0xaa, 0x48, 0x04, 0x91, 0x38, ++ 0xcf, 0x86, 0xfc, 0xa4, 0xa5, 0x0f, 0x47, 0x01, ++ 0x80, 0x1b, 0x30, 0xa3, 0xae, 0xe8, 0x1c, 0x2e, ++ 0xea, 0xcc, 0xf0, 0x03, 0x9f, 0x77, 0x4c, 0x8d, ++ 0x97, 0x76 ++}; ++static const u8 pkex_init_y_p521[66] = { ++ 0x00, 0xb3, 0x8e, 0x02, 0xe4, 0x2a, 0x63, 0x59, ++ 0x12, 0xc6, 0x10, 0xba, 0x3a, 0xf9, 0x02, 0x99, ++ 0x3f, 0x14, 0xf0, 0x40, 0xde, 0x5c, 0xc9, 0x8b, ++ 0x02, 0x55, 0xfa, 0x91, 0xb1, 0xcc, 0x6a, 0xbd, ++ 0xe5, 0x62, 0xc0, 0xc5, 0xe3, 0xa1, 0x57, 0x9f, ++ 0x08, 0x1a, 0xa6, 0xe2, 0xf8, 0x55, 0x90, 0xbf, ++ 0xf5, 0xa6, 0xc3, 0xd8, 0x52, 0x1f, 0xb7, 0x02, ++ 0x2e, 0x7c, 0xc8, 0xb3, 0x20, 0x1e, 0x79, 0x8d, ++ 0x03, 0xa8 ++}; ++static const u8 pkex_resp_x_p521[66] = { ++ 0x00, 0x79, 0xe4, 0x4d, 0x6b, 0x5e, 0x12, 0x0a, ++ 0x18, 0x2c, 0xb3, 0x05, 0x77, 0x0f, 0xc3, 0x44, ++ 0x1a, 0xcd, 0x78, 0x46, 0x14, 0xee, 0x46, 0x3f, ++ 0xab, 0xc9, 0x59, 0x7c, 0x85, 0xa0, 0xc2, 0xfb, ++ 0x02, 0x32, 0x99, 0xde, 0x5d, 0xe1, 0x0d, 0x48, ++ 0x2d, 0x71, 0x7d, 0x8d, 0x3f, 0x61, 0x67, 0x9e, ++ 0x2b, 0x8b, 0x12, 0xde, 0x10, 0x21, 0x55, 0x0a, ++ 0x5b, 0x2d, 0xe8, 0x05, 0x09, 0xf6, 0x20, 0x97, ++ 0x84, 0xb4 ++}; ++static const u8 pkex_resp_y_p521[66] = { ++ 0x00, 0x46, 0x63, 0x39, 0xbe, 0xcd, 0xa4, 0x2d, ++ 0xca, 0x27, 0x74, 0xd4, 0x1b, 0x91, 0x33, 0x20, ++ 0x83, 0xc7, 0x3b, 0xa4, 0x09, 0x8b, 0x8e, 0xa3, ++ 0x88, 0xe9, 0x75, 0x7f, 0x56, 0x7b, 0x38, 0x84, ++ 0x62, 0x02, 0x7c, 0x90, 0x51, 0x07, 0xdb, 0xe9, ++ 0xd0, 0xde, 0xda, 0x9a, 0x5d, 0xe5, 0x94, 0xd2, ++ 0xcf, 0x9d, 0x4c, 0x33, 0x91, 0xa6, 0xc3, 0x80, ++ 0xa7, 0x6e, 0x7e, 0x8d, 0xf8, 0x73, 0x6e, 0x53, ++ 0xce, 0xe1 ++}; ++ ++/* Brainpool P-256r1 */ ++static const u8 pkex_init_x_bp_p256r1[32] = { ++ 0x46, 0x98, 0x18, 0x6c, 0x27, 0xcd, 0x4b, 0x10, ++ 0x7d, 0x55, 0xa3, 0xdd, 0x89, 0x1f, 0x9f, 0xca, ++ 0xc7, 0x42, 0x5b, 0x8a, 0x23, 0xed, 0xf8, 0x75, ++ 0xac, 0xc7, 0xe9, 0x8d, 0xc2, 0x6f, 0xec, 0xd8 ++}; ++static const u8 pkex_init_y_bp_p256r1[32] = { ++ 0x93, 0xca, 0xef, 0xa9, 0x66, 0x3e, 0x87, 0xcd, ++ 0x52, 0x6e, 0x54, 0x13, 0xef, 0x31, 0x67, 0x30, ++ 0x15, 0x13, 0x9d, 0x6d, 0xc0, 0x95, 0x32, 0xbe, ++ 0x4f, 0xab, 0x5d, 0xf7, 0xbf, 0x5e, 0xaa, 0x0b ++}; ++static const u8 pkex_resp_x_bp_p256r1[32] = { ++ 0x90, 0x18, 0x84, 0xc9, 0xdc, 0xcc, 0xb5, 0x2f, ++ 0x4a, 0x3f, 0x4f, 0x18, 0x0a, 0x22, 0x56, 0x6a, ++ 0xa9, 0xef, 0xd4, 0xe6, 0xc3, 0x53, 0xc2, 0x1a, ++ 0x23, 0x54, 0xdd, 0x08, 0x7e, 0x10, 0xd8, 0xe3 ++}; ++static const u8 pkex_resp_y_bp_p256r1[32] = { ++ 0x2a, 0xfa, 0x98, 0x9b, 0xe3, 0xda, 0x30, 0xfd, ++ 0x32, 0x28, 0xcb, 0x66, 0xfb, 0x40, 0x7f, 0xf2, ++ 0xb2, 0x25, 0x80, 0x82, 0x44, 0x85, 0x13, 0x7e, ++ 0x4b, 0xb5, 0x06, 0xc0, 0x03, 0x69, 0x23, 0x64 ++}; ++ ++/* Brainpool P-384r1 */ ++static const u8 pkex_init_x_bp_p384r1[48] = { ++ 0x0a, 0x2c, 0xeb, 0x49, 0x5e, 0xb7, 0x23, 0xbd, ++ 0x20, 0x5b, 0xe0, 0x49, 0xdf, 0xcf, 0xcf, 0x19, ++ 0x37, 0x36, 0xe1, 0x2f, 0x59, 0xdb, 0x07, 0x06, ++ 0xb5, 0xeb, 0x2d, 0xae, 0xc2, 0xb2, 0x38, 0x62, ++ 0xa6, 0x73, 0x09, 0xa0, 0x6c, 0x0a, 0xa2, 0x30, ++ 0x99, 0xeb, 0xf7, 0x1e, 0x47, 0xb9, 0x5e, 0xbe ++}; ++static const u8 pkex_init_y_bp_p384r1[48] = { ++ 0x54, 0x76, 0x61, 0x65, 0x75, 0x5a, 0x2f, 0x99, ++ 0x39, 0x73, 0xca, 0x6c, 0xf9, 0xf7, 0x12, 0x86, ++ 0x54, 0xd5, 0xd4, 0xad, 0x45, 0x7b, 0xbf, 0x32, ++ 0xee, 0x62, 0x8b, 0x9f, 0x52, 0xe8, 0xa0, 0xc9, ++ 0xb7, 0x9d, 0xd1, 0x09, 0xb4, 0x79, 0x1c, 0x3e, ++ 0x1a, 0xbf, 0x21, 0x45, 0x66, 0x6b, 0x02, 0x52 ++}; ++static const u8 pkex_resp_x_bp_p384r1[48] = { ++ 0x03, 0xa2, 0x57, 0xef, 0xe8, 0x51, 0x21, 0xa0, ++ 0xc8, 0x9e, 0x21, 0x02, 0xb5, 0x9a, 0x36, 0x25, ++ 0x74, 0x22, 0xd1, 0xf2, 0x1b, 0xa8, 0x9a, 0x9b, ++ 0x97, 0xbc, 0x5a, 0xeb, 0x26, 0x15, 0x09, 0x71, ++ 0x77, 0x59, 0xec, 0x8b, 0xb7, 0xe1, 0xe8, 0xce, ++ 0x65, 0xb8, 0xaf, 0xf8, 0x80, 0xae, 0x74, 0x6c ++}; ++static const u8 pkex_resp_y_bp_p384r1[48] = { ++ 0x2f, 0xd9, 0x6a, 0xc7, 0x3e, 0xec, 0x76, 0x65, ++ 0x2d, 0x38, 0x7f, 0xec, 0x63, 0x26, 0x3f, 0x04, ++ 0xd8, 0x4e, 0xff, 0xe1, 0x0a, 0x51, 0x74, 0x70, ++ 0xe5, 0x46, 0x63, 0x7f, 0x5c, 0xc0, 0xd1, 0x7c, ++ 0xfb, 0x2f, 0xea, 0xe2, 0xd8, 0x0f, 0x84, 0xcb, ++ 0xe9, 0x39, 0x5c, 0x64, 0xfe, 0xcb, 0x2f, 0xf1 ++}; ++ ++/* Brainpool P-512r1 */ ++static const u8 pkex_init_x_bp_p512r1[64] = { ++ 0x4c, 0xe9, 0xb6, 0x1c, 0xe2, 0x00, 0x3c, 0x9c, ++ 0xa9, 0xc8, 0x56, 0x52, 0xaf, 0x87, 0x3e, 0x51, ++ 0x9c, 0xbb, 0x15, 0x31, 0x1e, 0xc1, 0x05, 0xfc, ++ 0x7c, 0x77, 0xd7, 0x37, 0x61, 0x27, 0xd0, 0x95, ++ 0x98, 0xee, 0x5d, 0xa4, 0x3d, 0x09, 0xdb, 0x3d, ++ 0xfa, 0x89, 0x9e, 0x7f, 0xa6, 0xa6, 0x9c, 0xff, ++ 0x83, 0x5c, 0x21, 0x6c, 0x3e, 0xf2, 0xfe, 0xdc, ++ 0x63, 0xe4, 0xd1, 0x0e, 0x75, 0x45, 0x69, 0x0f ++}; ++static const u8 pkex_init_y_bp_p512r1[64] = { ++ 0x50, 0xb5, 0x9b, 0xfa, 0x45, 0x67, 0x75, 0x94, ++ 0x44, 0xe7, 0x68, 0xb0, 0xeb, 0x3e, 0xb3, 0xb8, ++ 0xf9, 0x99, 0x05, 0xef, 0xae, 0x6c, 0xbc, 0xe3, ++ 0xe1, 0xd2, 0x51, 0x54, 0xdf, 0x59, 0xd4, 0x45, ++ 0x41, 0x3a, 0xa8, 0x0b, 0x76, 0x32, 0x44, 0x0e, ++ 0x07, 0x60, 0x3a, 0x6e, 0xbe, 0xfe, 0xe0, 0x58, ++ 0x52, 0xa0, 0xaa, 0x8b, 0xd8, 0x5b, 0xf2, 0x71, ++ 0x11, 0x9a, 0x9e, 0x8f, 0x1a, 0xd1, 0xc9, 0x99 ++}; ++static const u8 pkex_resp_x_bp_p512r1[64] = { ++ 0x2a, 0x60, 0x32, 0x27, 0xa1, 0xe6, 0x94, 0x72, ++ 0x1c, 0x48, 0xbe, 0xc5, 0x77, 0x14, 0x30, 0x76, ++ 0xe4, 0xbf, 0xf7, 0x7b, 0xc5, 0xfd, 0xdf, 0x19, ++ 0x1e, 0x0f, 0xdf, 0x1c, 0x40, 0xfa, 0x34, 0x9e, ++ 0x1f, 0x42, 0x24, 0xa3, 0x2c, 0xd5, 0xc7, 0xc9, ++ 0x7b, 0x47, 0x78, 0x96, 0xf1, 0x37, 0x0e, 0x88, ++ 0xcb, 0xa6, 0x52, 0x29, 0xd7, 0xa8, 0x38, 0x29, ++ 0x8e, 0x6e, 0x23, 0x47, 0xd4, 0x4b, 0x70, 0x3e ++}; ++static const u8 pkex_resp_y_bp_p512r1[64] = { ++ 0x80, 0x1f, 0x43, 0xd2, 0x17, 0x35, 0xec, 0x81, ++ 0xd9, 0x4b, 0xdc, 0x81, 0x19, 0xd9, 0x5f, 0x68, ++ 0x16, 0x84, 0xfe, 0x63, 0x4b, 0x8d, 0x5d, 0xaa, ++ 0x88, 0x4a, 0x47, 0x48, 0xd4, 0xea, 0xab, 0x7d, ++ 0x6a, 0xbf, 0xe1, 0x28, 0x99, 0x6a, 0x87, 0x1c, ++ 0x30, 0xb4, 0x44, 0x2d, 0x75, 0xac, 0x35, 0x09, ++ 0x73, 0x24, 0x3d, 0xb4, 0x43, 0xb1, 0xc1, 0x56, ++ 0x56, 0xad, 0x30, 0x87, 0xf4, 0xc3, 0x00, 0xc7 ++}; ++ ++ ++static void dpp_debug_print_point(const char *title, const EC_GROUP *group, ++ const EC_POINT *point) ++{ ++ BIGNUM *x, *y; ++ BN_CTX *ctx; ++ char *x_str = NULL, *y_str = NULL; ++ ++ if (!wpa_debug_show_keys) ++ return; ++ ++ ctx = BN_CTX_new(); ++ x = BN_new(); ++ y = BN_new(); ++ if (!ctx || !x || !y || ++ EC_POINT_get_affine_coordinates_GFp(group, point, x, y, ctx) != 1) ++ goto fail; ++ ++ x_str = BN_bn2hex(x); ++ y_str = BN_bn2hex(y); ++ if (!x_str || !y_str) ++ goto fail; ++ ++ wpa_printf(MSG_DEBUG, "%s (%s,%s)", title, x_str, y_str); ++ ++fail: ++ OPENSSL_free(x_str); ++ OPENSSL_free(y_str); ++ BN_free(x); ++ BN_free(y); ++ BN_CTX_free(ctx); ++} ++ ++ ++static int dpp_hash_vector(const struct dpp_curve_params *curve, ++ size_t num_elem, const u8 *addr[], const size_t *len, ++ u8 *mac) ++{ ++ if (curve->hash_len == 32) ++ return sha256_vector(num_elem, addr, len, mac); ++ if (curve->hash_len == 48) ++ return sha384_vector(num_elem, addr, len, mac); ++ if (curve->hash_len == 64) ++ return sha512_vector(num_elem, addr, len, mac); ++ return -1; ++} ++ ++ ++static int dpp_hkdf_expand(size_t hash_len, const u8 *secret, size_t secret_len, ++ const char *label, u8 *out, size_t outlen) ++{ ++ if (hash_len == 32) ++ return hmac_sha256_kdf(secret, secret_len, NULL, ++ (const u8 *) label, os_strlen(label), ++ out, outlen); ++ if (hash_len == 48) ++ return hmac_sha384_kdf(secret, secret_len, NULL, ++ (const u8 *) label, os_strlen(label), ++ out, outlen); ++ if (hash_len == 64) ++ return hmac_sha512_kdf(secret, secret_len, NULL, ++ (const u8 *) label, os_strlen(label), ++ out, outlen); ++ return -1; ++} ++ ++ ++static int dpp_hmac_vector(size_t hash_len, const u8 *key, size_t key_len, ++ size_t num_elem, const u8 *addr[], ++ const size_t *len, u8 *mac) ++{ ++ if (hash_len == 32) ++ return hmac_sha256_vector(key, key_len, num_elem, addr, len, ++ mac); ++ if (hash_len == 48) ++ return hmac_sha384_vector(key, key_len, num_elem, addr, len, ++ mac); ++ if (hash_len == 64) ++ return hmac_sha512_vector(key, key_len, num_elem, addr, len, ++ mac); ++ return -1; ++} ++ ++ ++static int dpp_hmac(size_t hash_len, const u8 *key, size_t key_len, ++ const u8 *data, size_t data_len, u8 *mac) ++{ ++ if (hash_len == 32) ++ return hmac_sha256(key, key_len, data, data_len, mac); ++ if (hash_len == 48) ++ return hmac_sha384(key, key_len, data, data_len, mac); ++ if (hash_len == 64) ++ return hmac_sha512(key, key_len, data, data_len, mac); ++ return -1; ++} ++ ++ ++static int dpp_bn2bin_pad(const BIGNUM *bn, u8 *pos, size_t len) ++{ ++ int num_bytes, offset; ++ ++ num_bytes = BN_num_bytes(bn); ++ if ((size_t) num_bytes > len) ++ return -1; ++ offset = len - num_bytes; ++ os_memset(pos, 0, offset); ++ BN_bn2bin(bn, pos + offset); ++ return 0; ++} ++ ++ ++static struct wpabuf * dpp_get_pubkey_point(EVP_PKEY *pkey, int prefix) ++{ ++ int len, res; ++ EC_KEY *eckey; ++ struct wpabuf *buf; ++ unsigned char *pos; ++ ++ eckey = EVP_PKEY_get1_EC_KEY(pkey); ++ if (!eckey) ++ return NULL; ++ EC_KEY_set_conv_form(eckey, POINT_CONVERSION_UNCOMPRESSED); ++ len = i2o_ECPublicKey(eckey, NULL); ++ if (len <= 0) { ++ wpa_printf(MSG_ERROR, ++ "DDP: Failed to determine public key encoding length"); ++ EC_KEY_free(eckey); ++ return NULL; ++ } ++ ++ buf = wpabuf_alloc(len); ++ if (!buf) { ++ EC_KEY_free(eckey); ++ return NULL; ++ } ++ ++ pos = wpabuf_put(buf, len); ++ res = i2o_ECPublicKey(eckey, &pos); ++ EC_KEY_free(eckey); ++ if (res != len) { ++ wpa_printf(MSG_ERROR, ++ "DDP: Failed to encode public key (res=%d/%d)", ++ res, len); ++ wpabuf_free(buf); ++ return NULL; ++ } ++ ++ if (!prefix) { ++ /* Remove 0x04 prefix to match DPP definition */ ++ pos = wpabuf_mhead(buf); ++ os_memmove(pos, pos + 1, len - 1); ++ buf->used--; ++ } ++ ++ return buf; ++} ++ ++ ++static EVP_PKEY * dpp_set_pubkey_point_group(const EC_GROUP *group, ++ const u8 *buf_x, const u8 *buf_y, ++ size_t len) ++{ ++ EC_KEY *eckey = NULL; ++ BN_CTX *ctx; ++ EC_POINT *point = NULL; ++ BIGNUM *x = NULL, *y = NULL; ++ EVP_PKEY *pkey = NULL; ++ ++ ctx = BN_CTX_new(); ++ if (!ctx) { ++ wpa_printf(MSG_ERROR, "DPP: Out of memory"); ++ return NULL; ++ } ++ ++ point = EC_POINT_new(group); ++ x = BN_bin2bn(buf_x, len, NULL); ++ y = BN_bin2bn(buf_y, len, NULL); ++ if (!point || !x || !y) { ++ wpa_printf(MSG_ERROR, "DPP: Out of memory"); ++ goto fail; ++ } ++ ++ if (!EC_POINT_set_affine_coordinates_GFp(group, point, x, y, ctx)) { ++ wpa_printf(MSG_ERROR, ++ "DPP: OpenSSL: EC_POINT_set_affine_coordinates_GFp failed: %s", ++ ERR_error_string(ERR_get_error(), NULL)); ++ goto fail; ++ } ++ ++ if (!EC_POINT_is_on_curve(group, point, ctx) || ++ EC_POINT_is_at_infinity(group, point)) { ++ wpa_printf(MSG_ERROR, "DPP: Invalid point"); ++ goto fail; ++ } ++ dpp_debug_print_point("DPP: dpp_set_pubkey_point_group", group, point); ++ ++ eckey = EC_KEY_new(); ++ if (!eckey || ++ EC_KEY_set_group(eckey, group) != 1 || ++ EC_KEY_set_public_key(eckey, point) != 1) { ++ wpa_printf(MSG_ERROR, ++ "DPP: Failed to set EC_KEY: %s", ++ ERR_error_string(ERR_get_error(), NULL)); ++ goto fail; ++ } ++ EC_KEY_set_asn1_flag(eckey, OPENSSL_EC_NAMED_CURVE); ++ ++ pkey = EVP_PKEY_new(); ++ if (!pkey || EVP_PKEY_set1_EC_KEY(pkey, eckey) != 1) { ++ wpa_printf(MSG_ERROR, "DPP: Could not create EVP_PKEY"); ++ goto fail; ++ } ++ ++out: ++ BN_free(x); ++ BN_free(y); ++ EC_KEY_free(eckey); ++ EC_POINT_free(point); ++ BN_CTX_free(ctx); ++ return pkey; ++fail: ++ EVP_PKEY_free(pkey); ++ pkey = NULL; ++ goto out; ++} ++ ++ ++static EVP_PKEY * dpp_set_pubkey_point(EVP_PKEY *group_key, ++ const u8 *buf, size_t len) ++{ ++ EC_KEY *eckey; ++ const EC_GROUP *group; ++ EVP_PKEY *pkey = NULL; ++ ++ if (len & 1) ++ return NULL; ++ ++ eckey = EVP_PKEY_get1_EC_KEY(group_key); ++ if (!eckey) { ++ wpa_printf(MSG_ERROR, ++ "DPP: Could not get EC_KEY from group_key"); ++ return NULL; ++ } ++ ++ group = EC_KEY_get0_group(eckey); ++ if (group) ++ pkey = dpp_set_pubkey_point_group(group, buf, buf + len / 2, ++ len / 2); ++ else ++ wpa_printf(MSG_ERROR, "DPP: Could not get EC group"); ++ ++ EC_KEY_free(eckey); ++ return pkey; ++} ++ ++ ++static void dpp_auth_fail(struct dpp_authentication *auth, const char *txt) ++{ ++ wpa_msg(auth->msg_ctx, MSG_INFO, DPP_EVENT_FAIL "%s", txt); ++} ++ ++ ++struct wpabuf * dpp_alloc_msg(enum dpp_public_action_frame_type type, ++ size_t len) ++{ ++ struct wpabuf *msg; ++ ++ msg = wpabuf_alloc(8 + len); ++ if (!msg) ++ return NULL; ++ wpabuf_put_u8(msg, WLAN_ACTION_PUBLIC); ++ wpabuf_put_u8(msg, WLAN_PA_VENDOR_SPECIFIC); ++ wpabuf_put_be24(msg, OUI_WFA); ++ wpabuf_put_u8(msg, DPP_OUI_TYPE); ++ wpabuf_put_u8(msg, 1); /* Crypto Suite */ ++ wpabuf_put_u8(msg, type); ++ return msg; ++} ++ ++ ++const u8 * dpp_get_attr(const u8 *buf, size_t len, u16 req_id, u16 *ret_len) ++{ ++ u16 id, alen; ++ const u8 *pos = buf, *end = buf + len; ++ ++ while (end - pos >= 4) { ++ id = WPA_GET_LE16(pos); ++ pos += 2; ++ alen = WPA_GET_LE16(pos); ++ pos += 2; ++ if (alen > end - pos) ++ return NULL; ++ if (id == req_id) { ++ *ret_len = alen; ++ return pos; ++ } ++ pos += alen; ++ } ++ ++ return NULL; ++} ++ ++ ++int dpp_check_attrs(const u8 *buf, size_t len) ++{ ++ const u8 *pos, *end; ++ int wrapped_data = 0; ++ ++ pos = buf; ++ end = buf + len; ++ while (end - pos >= 4) { ++ u16 id, alen; ++ ++ id = WPA_GET_LE16(pos); ++ pos += 2; ++ alen = WPA_GET_LE16(pos); ++ pos += 2; ++ wpa_printf(MSG_MSGDUMP, "DPP: Attribute ID %04x len %u", ++ id, alen); ++ if (alen > end - pos) { ++ wpa_printf(MSG_DEBUG, ++ "DPP: Truncated message - not enough room for the attribute - dropped"); ++ return -1; ++ } ++ if (wrapped_data) { ++ wpa_printf(MSG_DEBUG, ++ "DPP: An unexpected attribute included after the Wrapped Data attribute"); ++ return -1; ++ } ++ if (id == DPP_ATTR_WRAPPED_DATA) ++ wrapped_data = 1; ++ pos += alen; ++ } ++ ++ if (end != pos) { ++ wpa_printf(MSG_DEBUG, ++ "DPP: Unexpected octets (%d) after the last attribute", ++ (int) (end - pos)); ++ return -1; ++ } ++ ++ return 0; ++} ++ ++ ++void dpp_bootstrap_info_free(struct dpp_bootstrap_info *info) ++{ ++ if (!info) ++ return; ++ os_free(info->uri); ++ os_free(info->info); ++ EVP_PKEY_free(info->pubkey); ++ os_free(info); ++} ++ ++ ++const char * dpp_bootstrap_type_txt(enum dpp_bootstrap_type type) ++{ ++ switch (type) { ++ case DPP_BOOTSTRAP_QR_CODE: ++ return "QRCODE"; ++ case DPP_BOOTSTRAP_PKEX: ++ return "PKEX"; ++ } ++ return "??"; ++} ++ ++ ++static int dpp_uri_valid_info(const char *info) ++{ ++ while (*info) { ++ unsigned char val = *info++; ++ ++ if (val < 0x20 || val > 0x7e || val == 0x3b) ++ return 0; ++ } ++ ++ return 1; ++} ++ ++ ++static int dpp_clone_uri(struct dpp_bootstrap_info *bi, const char *uri) ++{ ++ bi->uri = os_strdup(uri); ++ return bi->uri ? 0 : -1; ++} ++ ++ ++int dpp_parse_uri_chan_list(struct dpp_bootstrap_info *bi, ++ const char *chan_list) ++{ ++ const char *pos = chan_list; ++ int opclass, channel, freq; ++ ++ while (pos && *pos && *pos != ';') { ++ opclass = atoi(pos); ++ if (opclass <= 0) ++ goto fail; ++ pos = os_strchr(pos, '/'); ++ if (!pos) ++ goto fail; ++ pos++; ++ channel = atoi(pos); ++ if (channel <= 0) ++ goto fail; ++ while (*pos >= '0' && *pos <= '9') ++ pos++; ++ freq = ieee80211_chan_to_freq(NULL, opclass, channel); ++ wpa_printf(MSG_DEBUG, ++ "DPP: URI channel-list: opclass=%d channel=%d ==> freq=%d", ++ opclass, channel, freq); ++ if (freq < 0) { ++ wpa_printf(MSG_DEBUG, ++ "DPP: Ignore unknown URI channel-list channel (opclass=%d channel=%d)", ++ opclass, channel); ++ } else if (bi->num_freq == DPP_BOOTSTRAP_MAX_FREQ) { ++ wpa_printf(MSG_DEBUG, ++ "DPP: Too many channels in URI channel-list - ignore list"); ++ bi->num_freq = 0; ++ break; ++ } else { ++ bi->freq[bi->num_freq++] = freq; ++ } ++ ++ if (*pos == ';' || *pos == '\0') ++ break; ++ if (*pos != ',') ++ goto fail; ++ pos++; ++ } ++ ++ return 0; ++fail: ++ wpa_printf(MSG_DEBUG, "DPP: Invalid URI channel-list"); ++ return -1; ++} ++ ++ ++int dpp_parse_uri_mac(struct dpp_bootstrap_info *bi, const char *mac) ++{ ++ if (!mac) ++ return 0; ++ ++ if (hwaddr_aton2(mac, bi->mac_addr) < 0) { ++ wpa_printf(MSG_DEBUG, "DPP: Invalid URI mac"); ++ return -1; ++ } ++ ++ wpa_printf(MSG_DEBUG, "DPP: URI mac: " MACSTR, MAC2STR(bi->mac_addr)); ++ ++ return 0; ++} ++ ++ ++int dpp_parse_uri_info(struct dpp_bootstrap_info *bi, const char *info) ++{ ++ const char *end; ++ ++ if (!info) ++ return 0; ++ ++ end = os_strchr(info, ';'); ++ if (!end) ++ end = info + os_strlen(info); ++ bi->info = os_malloc(end - info + 1); ++ if (!bi->info) ++ return -1; ++ os_memcpy(bi->info, info, end - info); ++ bi->info[end - info] = '\0'; ++ wpa_printf(MSG_DEBUG, "DPP: URI(information): %s", bi->info); ++ if (!dpp_uri_valid_info(bi->info)) { ++ wpa_printf(MSG_DEBUG, "DPP: Invalid URI information payload"); ++ return -1; ++ } ++ ++ return 0; ++} ++ ++ ++static const struct dpp_curve_params * ++dpp_get_curve_oid(const ASN1_OBJECT *poid) ++{ ++ ASN1_OBJECT *oid; ++ int i; ++ ++ for (i = 0; dpp_curves[i].name; i++) { ++ oid = OBJ_txt2obj(dpp_curves[i].name, 0); ++ if (oid && OBJ_cmp(poid, oid) == 0) ++ return &dpp_curves[i]; ++ } ++ return NULL; ++} ++ ++ ++static const struct dpp_curve_params * dpp_get_curve_nid(int nid) ++{ ++ int i, tmp; ++ ++ if (!nid) ++ return NULL; ++ for (i = 0; dpp_curves[i].name; i++) { ++ tmp = OBJ_txt2nid(dpp_curves[i].name); ++ if (tmp == nid) ++ return &dpp_curves[i]; ++ } ++ return NULL; ++} ++ ++ ++static int dpp_parse_uri_pk(struct dpp_bootstrap_info *bi, const char *info) ++{ ++ const char *end; ++ u8 *data; ++ size_t data_len; ++ EVP_PKEY *pkey; ++ const unsigned char *p; ++ int res; ++ X509_PUBKEY *pub = NULL; ++ ASN1_OBJECT *ppkalg; ++ const unsigned char *pk; ++ int ppklen; ++ X509_ALGOR *pa; ++#if OPENSSL_VERSION_NUMBER < 0x10100000L || \ ++ (defined(LIBRESSL_VERSION_NUMBER) && \ ++ LIBRESSL_VERSION_NUMBER < 0x20800000L) ++ ASN1_OBJECT *pa_oid; ++#else ++ const ASN1_OBJECT *pa_oid; ++#endif ++ const void *pval; ++ int ptype; ++ const ASN1_OBJECT *poid; ++ char buf[100]; ++ ++ end = os_strchr(info, ';'); ++ if (!end) ++ return -1; ++ ++ data = base64_decode((const unsigned char *) info, end - info, ++ &data_len); ++ if (!data) { ++ wpa_printf(MSG_DEBUG, ++ "DPP: Invalid base64 encoding on URI public-key"); ++ return -1; ++ } ++ wpa_hexdump(MSG_DEBUG, "DPP: Base64 decoded URI public-key", ++ data, data_len); ++ ++ if (sha256_vector(1, (const u8 **) &data, &data_len, ++ bi->pubkey_hash) < 0) { ++ wpa_printf(MSG_DEBUG, "DPP: Failed to hash public key"); ++ os_free(data); ++ return -1; ++ } ++ wpa_hexdump(MSG_DEBUG, "DPP: Public key hash", ++ bi->pubkey_hash, SHA256_MAC_LEN); ++ ++ /* DER encoded ASN.1 SubjectPublicKeyInfo ++ * ++ * SubjectPublicKeyInfo ::= SEQUENCE { ++ * algorithm AlgorithmIdentifier, ++ * subjectPublicKey BIT STRING } ++ * ++ * AlgorithmIdentifier ::= SEQUENCE { ++ * algorithm OBJECT IDENTIFIER, ++ * parameters ANY DEFINED BY algorithm OPTIONAL } ++ * ++ * subjectPublicKey = compressed format public key per ANSI X9.63 ++ * algorithm = ecPublicKey (1.2.840.10045.2.1) ++ * parameters = shall be present and shall be OBJECT IDENTIFIER; e.g., ++ * prime256v1 (1.2.840.10045.3.1.7) ++ */ ++ ++ p = data; ++ pkey = d2i_PUBKEY(NULL, &p, data_len); ++ os_free(data); ++ ++ if (!pkey) { ++ wpa_printf(MSG_DEBUG, ++ "DPP: Could not parse URI public-key SubjectPublicKeyInfo"); ++ return -1; ++ } ++ ++ if (EVP_PKEY_type(EVP_PKEY_id(pkey)) != EVP_PKEY_EC) { ++ wpa_printf(MSG_DEBUG, ++ "DPP: SubjectPublicKeyInfo does not describe an EC key"); ++ EVP_PKEY_free(pkey); ++ return -1; ++ } ++ ++ res = X509_PUBKEY_set(&pub, pkey); ++ if (res != 1) { ++ wpa_printf(MSG_DEBUG, "DPP: Could not set pubkey"); ++ goto fail; ++ } ++ ++ res = X509_PUBKEY_get0_param(&ppkalg, &pk, &ppklen, &pa, pub); ++ if (res != 1) { ++ wpa_printf(MSG_DEBUG, ++ "DPP: Could not extract SubjectPublicKeyInfo parameters"); ++ goto fail; ++ } ++ res = OBJ_obj2txt(buf, sizeof(buf), ppkalg, 0); ++ if (res < 0 || (size_t) res >= sizeof(buf)) { ++ wpa_printf(MSG_DEBUG, ++ "DPP: Could not extract SubjectPublicKeyInfo algorithm"); ++ goto fail; ++ } ++ wpa_printf(MSG_DEBUG, "DPP: URI subjectPublicKey algorithm: %s", buf); ++ if (os_strcmp(buf, "id-ecPublicKey") != 0) { ++ wpa_printf(MSG_DEBUG, ++ "DPP: Unsupported SubjectPublicKeyInfo algorithm"); ++ goto fail; ++ } ++ ++ X509_ALGOR_get0(&pa_oid, &ptype, (void *) &pval, pa); ++ if (ptype != V_ASN1_OBJECT) { ++ wpa_printf(MSG_DEBUG, ++ "DPP: SubjectPublicKeyInfo parameters did not contain an OID"); ++ goto fail; ++ } ++ poid = pval; ++ res = OBJ_obj2txt(buf, sizeof(buf), poid, 0); ++ if (res < 0 || (size_t) res >= sizeof(buf)) { ++ wpa_printf(MSG_DEBUG, ++ "DPP: Could not extract SubjectPublicKeyInfo parameters OID"); ++ goto fail; ++ } ++ wpa_printf(MSG_DEBUG, "DPP: URI subjectPublicKey parameters: %s", buf); ++ bi->curve = dpp_get_curve_oid(poid); ++ if (!bi->curve) { ++ wpa_printf(MSG_DEBUG, ++ "DPP: Unsupported SubjectPublicKeyInfo curve: %s", ++ buf); ++ goto fail; ++ } ++ ++ wpa_hexdump(MSG_DEBUG, "DPP: URI subjectPublicKey", pk, ppklen); ++ ++ X509_PUBKEY_free(pub); ++ bi->pubkey = pkey; ++ return 0; ++fail: ++ X509_PUBKEY_free(pub); ++ EVP_PKEY_free(pkey); ++ return -1; ++} ++ ++ ++static struct dpp_bootstrap_info * dpp_parse_uri(const char *uri) ++{ ++ const char *pos = uri; ++ const char *end; ++ const char *chan_list = NULL, *mac = NULL, *info = NULL, *pk = NULL; ++ struct dpp_bootstrap_info *bi; ++ ++ wpa_hexdump_ascii(MSG_DEBUG, "DPP: URI", uri, os_strlen(uri)); ++ ++ if (os_strncmp(pos, "DPP:", 4) != 0) { ++ wpa_printf(MSG_INFO, "DPP: Not a DPP URI"); ++ return NULL; ++ } ++ pos += 4; ++ ++ for (;;) { ++ end = os_strchr(pos, ';'); ++ if (!end) ++ break; ++ ++ if (end == pos) { ++ /* Handle terminating ";;" and ignore unexpected ";" ++ * for parsing robustness. */ ++ pos++; ++ continue; ++ } ++ ++ if (pos[0] == 'C' && pos[1] == ':' && !chan_list) ++ chan_list = pos + 2; ++ else if (pos[0] == 'M' && pos[1] == ':' && !mac) ++ mac = pos + 2; ++ else if (pos[0] == 'I' && pos[1] == ':' && !info) ++ info = pos + 2; ++ else if (pos[0] == 'K' && pos[1] == ':' && !pk) ++ pk = pos + 2; ++ else ++ wpa_hexdump_ascii(MSG_DEBUG, ++ "DPP: Ignore unrecognized URI parameter", ++ pos, end - pos); ++ pos = end + 1; ++ } ++ ++ if (!pk) { ++ wpa_printf(MSG_INFO, "DPP: URI missing public-key"); ++ return NULL; ++ } ++ ++ bi = os_zalloc(sizeof(*bi)); ++ if (!bi) ++ return NULL; ++ ++ if (dpp_clone_uri(bi, uri) < 0 || ++ dpp_parse_uri_chan_list(bi, chan_list) < 0 || ++ dpp_parse_uri_mac(bi, mac) < 0 || ++ dpp_parse_uri_info(bi, info) < 0 || ++ dpp_parse_uri_pk(bi, pk) < 0) { ++ dpp_bootstrap_info_free(bi); ++ bi = NULL; ++ } ++ ++ return bi; ++} ++ ++ ++struct dpp_bootstrap_info * dpp_parse_qr_code(const char *uri) ++{ ++ struct dpp_bootstrap_info *bi; ++ ++ bi = dpp_parse_uri(uri); ++ if (bi) ++ bi->type = DPP_BOOTSTRAP_QR_CODE; ++ return bi; ++} ++ ++ ++static void dpp_debug_print_key(const char *title, EVP_PKEY *key) ++{ ++ EC_KEY *eckey; ++ BIO *out; ++ size_t rlen; ++ char *txt; ++ int res; ++ unsigned char *der = NULL; ++ int der_len; ++ const EC_GROUP *group; ++ const EC_POINT *point; ++ ++ out = BIO_new(BIO_s_mem()); ++ if (!out) ++ return; ++ ++ EVP_PKEY_print_private(out, key, 0, NULL); ++ rlen = BIO_ctrl_pending(out); ++ txt = os_malloc(rlen + 1); ++ if (txt) { ++ res = BIO_read(out, txt, rlen); ++ if (res > 0) { ++ txt[res] = '\0'; ++ wpa_printf(MSG_DEBUG, "%s: %s", title, txt); ++ } ++ os_free(txt); ++ } ++ BIO_free(out); ++ ++ eckey = EVP_PKEY_get1_EC_KEY(key); ++ if (!eckey) ++ return; ++ ++ group = EC_KEY_get0_group(eckey); ++ point = EC_KEY_get0_public_key(eckey); ++ if (group && point) ++ dpp_debug_print_point(title, group, point); ++ ++ der_len = i2d_ECPrivateKey(eckey, &der); ++ if (der_len > 0) ++ wpa_hexdump_key(MSG_DEBUG, "DPP: ECPrivateKey", der, der_len); ++ OPENSSL_free(der); ++ if (der_len <= 0) { ++ der = NULL; ++ der_len = i2d_EC_PUBKEY(eckey, &der); ++ if (der_len > 0) ++ wpa_hexdump(MSG_DEBUG, "DPP: EC_PUBKEY", der, der_len); ++ OPENSSL_free(der); ++ } ++ ++ EC_KEY_free(eckey); ++} ++ ++ ++static EVP_PKEY * dpp_gen_keypair(const struct dpp_curve_params *curve) ++{ ++ EVP_PKEY_CTX *kctx = NULL; ++ EC_KEY *ec_params; ++ EVP_PKEY *params = NULL, *key = NULL; ++ int nid; ++ ++ wpa_printf(MSG_DEBUG, "DPP: Generating a keypair"); ++ ++ nid = OBJ_txt2nid(curve->name); ++ if (nid == NID_undef) { ++ wpa_printf(MSG_INFO, "DPP: Unsupported curve %s", curve->name); ++ return NULL; ++ } ++ ++ ec_params = EC_KEY_new_by_curve_name(nid); ++ if (!ec_params) { ++ wpa_printf(MSG_ERROR, ++ "DPP: Failed to generate EC_KEY parameters"); ++ goto fail; ++ } ++ EC_KEY_set_asn1_flag(ec_params, OPENSSL_EC_NAMED_CURVE); ++ params = EVP_PKEY_new(); ++ if (!params || EVP_PKEY_set1_EC_KEY(params, ec_params) != 1) { ++ wpa_printf(MSG_ERROR, ++ "DPP: Failed to generate EVP_PKEY parameters"); ++ goto fail; ++ } ++ ++ kctx = EVP_PKEY_CTX_new(params, NULL); ++ if (!kctx || ++ EVP_PKEY_keygen_init(kctx) != 1 || ++ EVP_PKEY_keygen(kctx, &key) != 1) { ++ wpa_printf(MSG_ERROR, "DPP: Failed to generate EC key"); ++ goto fail; ++ } ++ ++ if (wpa_debug_show_keys) ++ dpp_debug_print_key("Own generated key", key); ++ ++ EVP_PKEY_free(params); ++ EVP_PKEY_CTX_free(kctx); ++ return key; ++fail: ++ EVP_PKEY_CTX_free(kctx); ++ EVP_PKEY_free(params); ++ return NULL; ++} ++ ++ ++static const struct dpp_curve_params * ++dpp_get_curve_name(const char *name) ++{ ++ int i; ++ ++ for (i = 0; dpp_curves[i].name; i++) { ++ if (os_strcmp(name, dpp_curves[i].name) == 0 || ++ (dpp_curves[i].jwk_crv && ++ os_strcmp(name, dpp_curves[i].jwk_crv) == 0)) ++ return &dpp_curves[i]; ++ } ++ return NULL; ++} ++ ++ ++static const struct dpp_curve_params * ++dpp_get_curve_jwk_crv(const char *name) ++{ ++ int i; ++ ++ for (i = 0; dpp_curves[i].name; i++) { ++ if (dpp_curves[i].jwk_crv && ++ os_strcmp(name, dpp_curves[i].jwk_crv) == 0) ++ return &dpp_curves[i]; ++ } ++ return NULL; ++} ++ ++ ++static EVP_PKEY * dpp_set_keypair(const struct dpp_curve_params **curve, ++ const u8 *privkey, size_t privkey_len) ++{ ++ EVP_PKEY *pkey; ++ EC_KEY *eckey; ++ const EC_GROUP *group; ++ int nid; ++ ++ pkey = EVP_PKEY_new(); ++ if (!pkey) ++ return NULL; ++ eckey = d2i_ECPrivateKey(NULL, &privkey, privkey_len); ++ if (!eckey) { ++ wpa_printf(MSG_INFO, ++ "DPP: OpenSSL: d2i_ECPrivateKey() failed: %s", ++ ERR_error_string(ERR_get_error(), NULL)); ++ EVP_PKEY_free(pkey); ++ return NULL; ++ } ++ group = EC_KEY_get0_group(eckey); ++ if (!group) { ++ EC_KEY_free(eckey); ++ EVP_PKEY_free(pkey); ++ return NULL; ++ } ++ nid = EC_GROUP_get_curve_name(group); ++ *curve = dpp_get_curve_nid(nid); ++ if (!*curve) { ++ wpa_printf(MSG_INFO, ++ "DPP: Unsupported curve (nid=%d) in pre-assigned key", ++ nid); ++ EC_KEY_free(eckey); ++ EVP_PKEY_free(pkey); ++ return NULL; ++ } ++ ++ if (EVP_PKEY_assign_EC_KEY(pkey, eckey) != 1) { ++ EC_KEY_free(eckey); ++ EVP_PKEY_free(pkey); ++ return NULL; ++ } ++ return pkey; ++} ++ ++ ++typedef struct { ++ /* AlgorithmIdentifier ecPublicKey with optional parameters present ++ * as an OID identifying the curve */ ++ X509_ALGOR *alg; ++ /* Compressed format public key per ANSI X9.63 */ ++ ASN1_BIT_STRING *pub_key; ++} DPP_BOOTSTRAPPING_KEY; ++ ++ASN1_SEQUENCE(DPP_BOOTSTRAPPING_KEY) = { ++ ASN1_SIMPLE(DPP_BOOTSTRAPPING_KEY, alg, X509_ALGOR), ++ ASN1_SIMPLE(DPP_BOOTSTRAPPING_KEY, pub_key, ASN1_BIT_STRING) ++} ASN1_SEQUENCE_END(DPP_BOOTSTRAPPING_KEY); ++ ++IMPLEMENT_ASN1_FUNCTIONS(DPP_BOOTSTRAPPING_KEY); ++ ++ ++static struct wpabuf * dpp_bootstrap_key_der(EVP_PKEY *key) ++{ ++ unsigned char *der = NULL; ++ int der_len; ++ EC_KEY *eckey; ++ struct wpabuf *ret = NULL; ++ size_t len; ++ const EC_GROUP *group; ++ const EC_POINT *point; ++ BN_CTX *ctx; ++ DPP_BOOTSTRAPPING_KEY *bootstrap = NULL; ++ int nid; ++ ++ ctx = BN_CTX_new(); ++ eckey = EVP_PKEY_get1_EC_KEY(key); ++ if (!ctx || !eckey) ++ goto fail; ++ ++ group = EC_KEY_get0_group(eckey); ++ point = EC_KEY_get0_public_key(eckey); ++ if (!group || !point) ++ goto fail; ++ dpp_debug_print_point("DPP: bootstrap public key", group, point); ++ nid = EC_GROUP_get_curve_name(group); ++ ++ bootstrap = DPP_BOOTSTRAPPING_KEY_new(); ++ if (!bootstrap || ++ X509_ALGOR_set0(bootstrap->alg, OBJ_nid2obj(EVP_PKEY_EC), ++ V_ASN1_OBJECT, (void *) OBJ_nid2obj(nid)) != 1) ++ goto fail; ++ ++ len = EC_POINT_point2oct(group, point, POINT_CONVERSION_COMPRESSED, ++ NULL, 0, ctx); ++ if (len == 0) ++ goto fail; ++ ++ der = OPENSSL_malloc(len); ++ if (!der) ++ goto fail; ++ len = EC_POINT_point2oct(group, point, POINT_CONVERSION_COMPRESSED, ++ der, len, ctx); ++ ++ OPENSSL_free(bootstrap->pub_key->data); ++ bootstrap->pub_key->data = der; ++ der = NULL; ++ bootstrap->pub_key->length = len; ++ /* No unused bits */ ++ bootstrap->pub_key->flags &= ~(ASN1_STRING_FLAG_BITS_LEFT | 0x07); ++ bootstrap->pub_key->flags |= ASN1_STRING_FLAG_BITS_LEFT; ++ ++ der_len = i2d_DPP_BOOTSTRAPPING_KEY(bootstrap, &der); ++ if (der_len <= 0) { ++ wpa_printf(MSG_ERROR, ++ "DDP: Failed to build DER encoded public key"); ++ goto fail; ++ } ++ ++ ret = wpabuf_alloc_copy(der, der_len); ++fail: ++ DPP_BOOTSTRAPPING_KEY_free(bootstrap); ++ OPENSSL_free(der); ++ EC_KEY_free(eckey); ++ BN_CTX_free(ctx); ++ return ret; ++} ++ ++ ++int dpp_bootstrap_key_hash(struct dpp_bootstrap_info *bi) ++{ ++ struct wpabuf *der; ++ int res; ++ const u8 *addr[1]; ++ size_t len[1]; ++ ++ der = dpp_bootstrap_key_der(bi->pubkey); ++ if (!der) ++ return -1; ++ wpa_hexdump_buf(MSG_DEBUG, "DPP: Compressed public key (DER)", ++ der); ++ ++ addr[0] = wpabuf_head(der); ++ len[0] = wpabuf_len(der); ++ res = sha256_vector(1, addr, len, bi->pubkey_hash); ++ if (res < 0) ++ wpa_printf(MSG_DEBUG, "DPP: Failed to hash public key"); ++ else ++ wpa_hexdump(MSG_DEBUG, "DPP: Public key hash", bi->pubkey_hash, ++ SHA256_MAC_LEN); ++ wpabuf_free(der); ++ return res; ++} ++ ++ ++char * dpp_keygen(struct dpp_bootstrap_info *bi, const char *curve, ++ const u8 *privkey, size_t privkey_len) ++{ ++ unsigned char *base64 = NULL; ++ char *pos, *end; ++ size_t len; ++ struct wpabuf *der = NULL; ++ const u8 *addr[1]; ++ int res; ++ ++ if (!curve) { ++ bi->curve = &dpp_curves[0]; ++ } else { ++ bi->curve = dpp_get_curve_name(curve); ++ if (!bi->curve) { ++ wpa_printf(MSG_INFO, "DPP: Unsupported curve: %s", ++ curve); ++ return NULL; ++ } ++ } ++ if (privkey) ++ bi->pubkey = dpp_set_keypair(&bi->curve, privkey, privkey_len); ++ else ++ bi->pubkey = dpp_gen_keypair(bi->curve); ++ if (!bi->pubkey) ++ goto fail; ++ bi->own = 1; ++ ++ der = dpp_bootstrap_key_der(bi->pubkey); ++ if (!der) ++ goto fail; ++ wpa_hexdump_buf(MSG_DEBUG, "DPP: Compressed public key (DER)", ++ der); ++ ++ addr[0] = wpabuf_head(der); ++ len = wpabuf_len(der); ++ res = sha256_vector(1, addr, &len, bi->pubkey_hash); ++ if (res < 0) { ++ wpa_printf(MSG_DEBUG, "DPP: Failed to hash public key"); ++ goto fail; ++ } ++ wpa_hexdump(MSG_DEBUG, "DPP: Public key hash", bi->pubkey_hash, ++ SHA256_MAC_LEN); ++ ++ base64 = base64_encode(wpabuf_head(der), wpabuf_len(der), &len); ++ wpabuf_free(der); ++ der = NULL; ++ if (!base64) ++ goto fail; ++ pos = (char *) base64; ++ end = pos + len; ++ for (;;) { ++ pos = os_strchr(pos, '\n'); ++ if (!pos) ++ break; ++ os_memmove(pos, pos + 1, end - pos); ++ } ++ return (char *) base64; ++fail: ++ os_free(base64); ++ wpabuf_free(der); ++ return NULL; ++} ++ ++ ++static int dpp_derive_k1(const u8 *Mx, size_t Mx_len, u8 *k1, ++ unsigned int hash_len) ++{ ++ u8 salt[DPP_MAX_HASH_LEN], prk[DPP_MAX_HASH_LEN]; ++ const char *info = "first intermediate key"; ++ int res; ++ ++ /* k1 = HKDF(<>, "first intermediate key", M.x) */ ++ ++ /* HKDF-Extract(<>, M.x) */ ++ os_memset(salt, 0, hash_len); ++ if (dpp_hmac(hash_len, salt, hash_len, Mx, Mx_len, prk) < 0) ++ return -1; ++ wpa_hexdump_key(MSG_DEBUG, "DPP: PRK = HKDF-Extract(<>, IKM=M.x)", ++ prk, hash_len); ++ ++ /* HKDF-Expand(PRK, info, L) */ ++ res = dpp_hkdf_expand(hash_len, prk, hash_len, info, k1, hash_len); ++ os_memset(prk, 0, hash_len); ++ if (res < 0) ++ return -1; ++ ++ wpa_hexdump_key(MSG_DEBUG, "DPP: k1 = HKDF-Expand(PRK, info, L)", ++ k1, hash_len); ++ return 0; ++} ++ ++ ++static int dpp_derive_k2(const u8 *Nx, size_t Nx_len, u8 *k2, ++ unsigned int hash_len) ++{ ++ u8 salt[DPP_MAX_HASH_LEN], prk[DPP_MAX_HASH_LEN]; ++ const char *info = "second intermediate key"; ++ int res; ++ ++ /* k2 = HKDF(<>, "second intermediate key", N.x) */ ++ ++ /* HKDF-Extract(<>, N.x) */ ++ os_memset(salt, 0, hash_len); ++ res = dpp_hmac(hash_len, salt, hash_len, Nx, Nx_len, prk); ++ if (res < 0) ++ return -1; ++ wpa_hexdump_key(MSG_DEBUG, "DPP: PRK = HKDF-Extract(<>, IKM=N.x)", ++ prk, hash_len); ++ ++ /* HKDF-Expand(PRK, info, L) */ ++ res = dpp_hkdf_expand(hash_len, prk, hash_len, info, k2, hash_len); ++ os_memset(prk, 0, hash_len); ++ if (res < 0) ++ return -1; ++ ++ wpa_hexdump_key(MSG_DEBUG, "DPP: k2 = HKDF-Expand(PRK, info, L)", ++ k2, hash_len); ++ return 0; ++} ++ ++ ++static int dpp_derive_ke(struct dpp_authentication *auth, u8 *ke, ++ unsigned int hash_len) ++{ ++ size_t nonce_len; ++ u8 nonces[2 * DPP_MAX_NONCE_LEN]; ++ const char *info_ke = "DPP Key"; ++ u8 prk[DPP_MAX_HASH_LEN]; ++ int res; ++ const u8 *addr[3]; ++ size_t len[3]; ++ size_t num_elem = 0; ++ ++ if (!auth->Mx_len || !auth->Nx_len) { ++ wpa_printf(MSG_DEBUG, ++ "DPP: Mx/Nx not available - cannot derive ke"); ++ return -1; ++ } ++ ++ /* ke = HKDF(I-nonce | R-nonce, "DPP Key", M.x | N.x [| L.x]) */ ++ ++ /* HKDF-Extract(I-nonce | R-nonce, M.x | N.x [| L.x]) */ ++ nonce_len = auth->curve->nonce_len; ++ os_memcpy(nonces, auth->i_nonce, nonce_len); ++ os_memcpy(&nonces[nonce_len], auth->r_nonce, nonce_len); ++ addr[num_elem] = auth->Mx; ++ len[num_elem] = auth->Mx_len; ++ num_elem++; ++ addr[num_elem] = auth->Nx; ++ len[num_elem] = auth->Nx_len; ++ num_elem++; ++ if (auth->peer_bi && auth->own_bi) { ++ if (!auth->Lx_len) { ++ wpa_printf(MSG_DEBUG, ++ "DPP: Lx not available - cannot derive ke"); ++ return -1; ++ } ++ addr[num_elem] = auth->Lx; ++ len[num_elem] = auth->secret_len; ++ num_elem++; ++ } ++ res = dpp_hmac_vector(hash_len, nonces, 2 * nonce_len, ++ num_elem, addr, len, prk); ++ if (res < 0) ++ return -1; ++ wpa_hexdump_key(MSG_DEBUG, "DPP: PRK = HKDF-Extract(<>, IKM)", ++ prk, hash_len); ++ ++ /* HKDF-Expand(PRK, info, L) */ ++ res = dpp_hkdf_expand(hash_len, prk, hash_len, info_ke, ke, hash_len); ++ os_memset(prk, 0, hash_len); ++ if (res < 0) ++ return -1; ++ ++ wpa_hexdump_key(MSG_DEBUG, "DPP: ke = HKDF-Expand(PRK, info, L)", ++ ke, hash_len); ++ return 0; ++} ++ ++ ++static void dpp_build_attr_status(struct wpabuf *msg, ++ enum dpp_status_error status) ++{ ++ wpa_printf(MSG_DEBUG, "DPP: Status %d", status); ++ wpabuf_put_le16(msg, DPP_ATTR_STATUS); ++ wpabuf_put_le16(msg, 1); ++ wpabuf_put_u8(msg, status); ++} ++ ++ ++static void dpp_build_attr_r_bootstrap_key_hash(struct wpabuf *msg, ++ const u8 *hash) ++{ ++ if (hash) { ++ wpa_printf(MSG_DEBUG, "DPP: R-Bootstrap Key Hash"); ++ wpabuf_put_le16(msg, DPP_ATTR_R_BOOTSTRAP_KEY_HASH); ++ wpabuf_put_le16(msg, SHA256_MAC_LEN); ++ wpabuf_put_data(msg, hash, SHA256_MAC_LEN); ++ } ++} ++ ++ ++static void dpp_build_attr_i_bootstrap_key_hash(struct wpabuf *msg, ++ const u8 *hash) ++{ ++ if (hash) { ++ wpa_printf(MSG_DEBUG, "DPP: I-Bootstrap Key Hash"); ++ wpabuf_put_le16(msg, DPP_ATTR_I_BOOTSTRAP_KEY_HASH); ++ wpabuf_put_le16(msg, SHA256_MAC_LEN); ++ wpabuf_put_data(msg, hash, SHA256_MAC_LEN); ++ } ++} ++ ++ ++static struct wpabuf * dpp_auth_build_req(struct dpp_authentication *auth, ++ const struct wpabuf *pi, ++ size_t nonce_len, ++ const u8 *r_pubkey_hash, ++ const u8 *i_pubkey_hash, ++ unsigned int neg_freq) ++{ ++ struct wpabuf *msg; ++ u8 clear[4 + DPP_MAX_NONCE_LEN + 4 + 1]; ++ u8 wrapped_data[4 + DPP_MAX_NONCE_LEN + 4 + 1 + AES_BLOCK_SIZE]; ++ u8 *pos; ++ const u8 *addr[2]; ++ size_t len[2], siv_len, attr_len; ++ u8 *attr_start, *attr_end; ++ ++ /* Build DPP Authentication Request frame attributes */ ++ attr_len = 2 * (4 + SHA256_MAC_LEN) + 4 + (pi ? wpabuf_len(pi) : 0) + ++ 4 + sizeof(wrapped_data); ++ if (neg_freq > 0) ++ attr_len += 4 + 2; ++#ifdef CONFIG_DPP2 ++ attr_len += 5; ++#endif /* CONFIG_DPP2 */ ++#ifdef CONFIG_TESTING_OPTIONS ++ if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_AUTH_REQ) ++ attr_len += 5; ++#endif /* CONFIG_TESTING_OPTIONS */ ++ msg = dpp_alloc_msg(DPP_PA_AUTHENTICATION_REQ, attr_len); ++ if (!msg) ++ return NULL; ++ ++ attr_start = wpabuf_put(msg, 0); ++ ++ /* Responder Bootstrapping Key Hash */ ++ dpp_build_attr_r_bootstrap_key_hash(msg, r_pubkey_hash); ++ ++ /* Initiator Bootstrapping Key Hash */ ++ dpp_build_attr_i_bootstrap_key_hash(msg, i_pubkey_hash); ++ ++ /* Initiator Protocol Key */ ++ if (pi) { ++ wpabuf_put_le16(msg, DPP_ATTR_I_PROTOCOL_KEY); ++ wpabuf_put_le16(msg, wpabuf_len(pi)); ++ wpabuf_put_buf(msg, pi); ++ } ++ ++ /* Channel */ ++ if (neg_freq > 0) { ++ u8 op_class, channel; ++ ++ if (ieee80211_freq_to_channel_ext(neg_freq, 0, 0, &op_class, ++ &channel) == ++ NUM_HOSTAPD_MODES) { ++ wpa_printf(MSG_INFO, ++ "DPP: Unsupported negotiation frequency request: %d", ++ neg_freq); ++ wpabuf_free(msg); ++ return NULL; ++ } ++ wpabuf_put_le16(msg, DPP_ATTR_CHANNEL); ++ wpabuf_put_le16(msg, 2); ++ wpabuf_put_u8(msg, op_class); ++ wpabuf_put_u8(msg, channel); ++ } ++ ++#ifdef CONFIG_DPP2 ++ /* Protocol Version */ ++ wpabuf_put_le16(msg, DPP_ATTR_PROTOCOL_VERSION); ++ wpabuf_put_le16(msg, 1); ++ wpabuf_put_u8(msg, 2); ++#endif /* CONFIG_DPP2 */ ++ ++#ifdef CONFIG_TESTING_OPTIONS ++ if (dpp_test == DPP_TEST_NO_WRAPPED_DATA_AUTH_REQ) { ++ wpa_printf(MSG_INFO, "DPP: TESTING - no Wrapped Data"); ++ goto skip_wrapped_data; ++ } ++#endif /* CONFIG_TESTING_OPTIONS */ ++ ++ /* Wrapped data ({I-nonce, I-capabilities}k1) */ ++ pos = clear; ++ ++#ifdef CONFIG_TESTING_OPTIONS ++ if (dpp_test == DPP_TEST_NO_I_NONCE_AUTH_REQ) { ++ wpa_printf(MSG_INFO, "DPP: TESTING - no I-nonce"); ++ goto skip_i_nonce; ++ } ++ if (dpp_test == DPP_TEST_INVALID_I_NONCE_AUTH_REQ) { ++ wpa_printf(MSG_INFO, "DPP: TESTING - invalid I-nonce"); ++ WPA_PUT_LE16(pos, DPP_ATTR_I_NONCE); ++ pos += 2; ++ WPA_PUT_LE16(pos, nonce_len - 1); ++ pos += 2; ++ os_memcpy(pos, auth->i_nonce, nonce_len - 1); ++ pos += nonce_len - 1; ++ goto skip_i_nonce; ++ } ++#endif /* CONFIG_TESTING_OPTIONS */ ++ ++ /* I-nonce */ ++ WPA_PUT_LE16(pos, DPP_ATTR_I_NONCE); ++ pos += 2; ++ WPA_PUT_LE16(pos, nonce_len); ++ pos += 2; ++ os_memcpy(pos, auth->i_nonce, nonce_len); ++ pos += nonce_len; ++ ++#ifdef CONFIG_TESTING_OPTIONS ++skip_i_nonce: ++ if (dpp_test == DPP_TEST_NO_I_CAPAB_AUTH_REQ) { ++ wpa_printf(MSG_INFO, "DPP: TESTING - no I-capab"); ++ goto skip_i_capab; ++ } ++#endif /* CONFIG_TESTING_OPTIONS */ ++ ++ /* I-capabilities */ ++ WPA_PUT_LE16(pos, DPP_ATTR_I_CAPABILITIES); ++ pos += 2; ++ WPA_PUT_LE16(pos, 1); ++ pos += 2; ++ auth->i_capab = auth->allowed_roles; ++ *pos++ = auth->i_capab; ++#ifdef CONFIG_TESTING_OPTIONS ++ if (dpp_test == DPP_TEST_ZERO_I_CAPAB) { ++ wpa_printf(MSG_INFO, "DPP: TESTING - zero I-capabilities"); ++ pos[-1] = 0; ++ } ++skip_i_capab: ++#endif /* CONFIG_TESTING_OPTIONS */ ++ ++ attr_end = wpabuf_put(msg, 0); ++ ++ /* OUI, OUI type, Crypto Suite, DPP frame type */ ++ addr[0] = wpabuf_head_u8(msg) + 2; ++ len[0] = 3 + 1 + 1 + 1; ++ wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]); ++ ++ /* Attributes before Wrapped Data */ ++ addr[1] = attr_start; ++ len[1] = attr_end - attr_start; ++ wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]); ++ ++ siv_len = pos - clear; ++ wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext", clear, siv_len); ++ if (aes_siv_encrypt(auth->k1, auth->curve->hash_len, clear, siv_len, ++ 2, addr, len, wrapped_data) < 0) { ++ wpabuf_free(msg); ++ return NULL; ++ } ++ siv_len += AES_BLOCK_SIZE; ++ wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext", ++ wrapped_data, siv_len); ++ ++ wpabuf_put_le16(msg, DPP_ATTR_WRAPPED_DATA); ++ wpabuf_put_le16(msg, siv_len); ++ wpabuf_put_data(msg, wrapped_data, siv_len); ++ ++#ifdef CONFIG_TESTING_OPTIONS ++ if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_AUTH_REQ) { ++ wpa_printf(MSG_INFO, "DPP: TESTING - attr after Wrapped Data"); ++ dpp_build_attr_status(msg, DPP_STATUS_OK); ++ } ++skip_wrapped_data: ++#endif /* CONFIG_TESTING_OPTIONS */ ++ ++ wpa_hexdump_buf(MSG_DEBUG, ++ "DPP: Authentication Request frame attributes", msg); ++ ++ return msg; ++} ++ ++ ++static struct wpabuf * dpp_auth_build_resp(struct dpp_authentication *auth, ++ enum dpp_status_error status, ++ const struct wpabuf *pr, ++ size_t nonce_len, ++ const u8 *r_pubkey_hash, ++ const u8 *i_pubkey_hash, ++ const u8 *r_nonce, const u8 *i_nonce, ++ const u8 *wrapped_r_auth, ++ size_t wrapped_r_auth_len, ++ const u8 *siv_key) ++{ ++ struct wpabuf *msg; ++#define DPP_AUTH_RESP_CLEAR_LEN 2 * (4 + DPP_MAX_NONCE_LEN) + 4 + 1 + \ ++ 4 + 4 + DPP_MAX_HASH_LEN + AES_BLOCK_SIZE ++ u8 clear[DPP_AUTH_RESP_CLEAR_LEN]; ++ u8 wrapped_data[DPP_AUTH_RESP_CLEAR_LEN + AES_BLOCK_SIZE]; ++ const u8 *addr[2]; ++ size_t len[2], siv_len, attr_len; ++ u8 *attr_start, *attr_end, *pos; ++ ++ auth->waiting_auth_conf = 1; ++ auth->auth_resp_tries = 0; ++ ++ /* Build DPP Authentication Response frame attributes */ ++ attr_len = 4 + 1 + 2 * (4 + SHA256_MAC_LEN) + ++ 4 + (pr ? wpabuf_len(pr) : 0) + 4 + sizeof(wrapped_data); ++#ifdef CONFIG_DPP2 ++ attr_len += 5; ++#endif /* CONFIG_DPP2 */ ++#ifdef CONFIG_TESTING_OPTIONS ++ if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_AUTH_RESP) ++ attr_len += 5; ++#endif /* CONFIG_TESTING_OPTIONS */ ++ msg = dpp_alloc_msg(DPP_PA_AUTHENTICATION_RESP, attr_len); ++ if (!msg) ++ return NULL; ++ ++ attr_start = wpabuf_put(msg, 0); ++ ++ /* DPP Status */ ++ if (status != 255) ++ dpp_build_attr_status(msg, status); ++ ++ /* Responder Bootstrapping Key Hash */ ++ dpp_build_attr_r_bootstrap_key_hash(msg, r_pubkey_hash); ++ ++ /* Initiator Bootstrapping Key Hash (mutual authentication) */ ++ dpp_build_attr_i_bootstrap_key_hash(msg, i_pubkey_hash); ++ ++ /* Responder Protocol Key */ ++ if (pr) { ++ wpabuf_put_le16(msg, DPP_ATTR_R_PROTOCOL_KEY); ++ wpabuf_put_le16(msg, wpabuf_len(pr)); ++ wpabuf_put_buf(msg, pr); ++ } ++ ++#ifdef CONFIG_DPP2 ++ /* Protocol Version */ ++ wpabuf_put_le16(msg, DPP_ATTR_PROTOCOL_VERSION); ++ wpabuf_put_le16(msg, 1); ++ wpabuf_put_u8(msg, 2); ++#endif /* CONFIG_DPP2 */ ++ ++ attr_end = wpabuf_put(msg, 0); ++ ++#ifdef CONFIG_TESTING_OPTIONS ++ if (dpp_test == DPP_TEST_NO_WRAPPED_DATA_AUTH_RESP) { ++ wpa_printf(MSG_INFO, "DPP: TESTING - no Wrapped Data"); ++ goto skip_wrapped_data; ++ } ++#endif /* CONFIG_TESTING_OPTIONS */ ++ ++ /* Wrapped data ({R-nonce, I-nonce, R-capabilities, {R-auth}ke}k2) */ ++ pos = clear; ++ ++ if (r_nonce) { ++ /* R-nonce */ ++ WPA_PUT_LE16(pos, DPP_ATTR_R_NONCE); ++ pos += 2; ++ WPA_PUT_LE16(pos, nonce_len); ++ pos += 2; ++ os_memcpy(pos, r_nonce, nonce_len); ++ pos += nonce_len; ++ } ++ ++ if (i_nonce) { ++ /* I-nonce */ ++ WPA_PUT_LE16(pos, DPP_ATTR_I_NONCE); ++ pos += 2; ++ WPA_PUT_LE16(pos, nonce_len); ++ pos += 2; ++ os_memcpy(pos, i_nonce, nonce_len); ++#ifdef CONFIG_TESTING_OPTIONS ++ if (dpp_test == DPP_TEST_I_NONCE_MISMATCH_AUTH_RESP) { ++ wpa_printf(MSG_INFO, "DPP: TESTING - I-nonce mismatch"); ++ pos[nonce_len / 2] ^= 0x01; ++ } ++#endif /* CONFIG_TESTING_OPTIONS */ ++ pos += nonce_len; ++ } ++ ++#ifdef CONFIG_TESTING_OPTIONS ++ if (dpp_test == DPP_TEST_NO_R_CAPAB_AUTH_RESP) { ++ wpa_printf(MSG_INFO, "DPP: TESTING - no R-capab"); ++ goto skip_r_capab; ++ } ++#endif /* CONFIG_TESTING_OPTIONS */ ++ ++ /* R-capabilities */ ++ WPA_PUT_LE16(pos, DPP_ATTR_R_CAPABILITIES); ++ pos += 2; ++ WPA_PUT_LE16(pos, 1); ++ pos += 2; ++ auth->r_capab = auth->configurator ? DPP_CAPAB_CONFIGURATOR : ++ DPP_CAPAB_ENROLLEE; ++ *pos++ = auth->r_capab; ++#ifdef CONFIG_TESTING_OPTIONS ++ if (dpp_test == DPP_TEST_ZERO_R_CAPAB) { ++ wpa_printf(MSG_INFO, "DPP: TESTING - zero R-capabilities"); ++ pos[-1] = 0; ++ } else if (dpp_test == DPP_TEST_INCOMPATIBLE_R_CAPAB_AUTH_RESP) { ++ wpa_printf(MSG_INFO, ++ "DPP: TESTING - incompatible R-capabilities"); ++ if ((auth->i_capab & DPP_CAPAB_ROLE_MASK) == ++ (DPP_CAPAB_CONFIGURATOR | DPP_CAPAB_ENROLLEE)) ++ pos[-1] = 0; ++ else ++ pos[-1] = auth->configurator ? DPP_CAPAB_ENROLLEE : ++ DPP_CAPAB_CONFIGURATOR; ++ } ++skip_r_capab: ++#endif /* CONFIG_TESTING_OPTIONS */ ++ ++ if (wrapped_r_auth) { ++ /* {R-auth}ke */ ++ WPA_PUT_LE16(pos, DPP_ATTR_WRAPPED_DATA); ++ pos += 2; ++ WPA_PUT_LE16(pos, wrapped_r_auth_len); ++ pos += 2; ++ os_memcpy(pos, wrapped_r_auth, wrapped_r_auth_len); ++ pos += wrapped_r_auth_len; ++ } ++ ++ /* OUI, OUI type, Crypto Suite, DPP frame type */ ++ addr[0] = wpabuf_head_u8(msg) + 2; ++ len[0] = 3 + 1 + 1 + 1; ++ wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]); ++ ++ /* Attributes before Wrapped Data */ ++ addr[1] = attr_start; ++ len[1] = attr_end - attr_start; ++ wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]); ++ ++ siv_len = pos - clear; ++ wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext", clear, siv_len); ++ if (aes_siv_encrypt(siv_key, auth->curve->hash_len, clear, siv_len, ++ 2, addr, len, wrapped_data) < 0) { ++ wpabuf_free(msg); ++ return NULL; ++ } ++ siv_len += AES_BLOCK_SIZE; ++ wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext", ++ wrapped_data, siv_len); ++ ++ wpabuf_put_le16(msg, DPP_ATTR_WRAPPED_DATA); ++ wpabuf_put_le16(msg, siv_len); ++ wpabuf_put_data(msg, wrapped_data, siv_len); ++ ++#ifdef CONFIG_TESTING_OPTIONS ++ if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_AUTH_RESP) { ++ wpa_printf(MSG_INFO, "DPP: TESTING - attr after Wrapped Data"); ++ dpp_build_attr_status(msg, DPP_STATUS_OK); ++ } ++skip_wrapped_data: ++#endif /* CONFIG_TESTING_OPTIONS */ ++ ++ wpa_hexdump_buf(MSG_DEBUG, ++ "DPP: Authentication Response frame attributes", msg); ++ return msg; ++} ++ ++ ++static int dpp_channel_ok_init(struct hostapd_hw_modes *own_modes, ++ u16 num_modes, unsigned int freq) ++{ ++ u16 m; ++ int c, flag; ++ ++ if (!own_modes || !num_modes) ++ return 1; ++ ++ for (m = 0; m < num_modes; m++) { ++ for (c = 0; c < own_modes[m].num_channels; c++) { ++ if ((unsigned int) own_modes[m].channels[c].freq != ++ freq) ++ continue; ++ flag = own_modes[m].channels[c].flag; ++ if (!(flag & (HOSTAPD_CHAN_DISABLED | ++ HOSTAPD_CHAN_NO_IR | ++ HOSTAPD_CHAN_RADAR))) ++ return 1; ++ } ++ } ++ ++ wpa_printf(MSG_DEBUG, "DPP: Peer channel %u MHz not supported", freq); ++ return 0; ++} ++ ++ ++static int freq_included(const unsigned int freqs[], unsigned int num, ++ unsigned int freq) ++{ ++ while (num > 0) { ++ if (freqs[--num] == freq) ++ return 1; ++ } ++ return 0; ++} ++ ++ ++static void freq_to_start(unsigned int freqs[], unsigned int num, ++ unsigned int freq) ++{ ++ unsigned int i; ++ ++ for (i = 0; i < num; i++) { ++ if (freqs[i] == freq) ++ break; ++ } ++ if (i == 0 || i >= num) ++ return; ++ os_memmove(&freqs[1], &freqs[0], i * sizeof(freqs[0])); ++ freqs[0] = freq; ++} ++ ++ ++static int dpp_channel_intersect(struct dpp_authentication *auth, ++ struct hostapd_hw_modes *own_modes, ++ u16 num_modes) ++{ ++ struct dpp_bootstrap_info *peer_bi = auth->peer_bi; ++ unsigned int i, freq; ++ ++ for (i = 0; i < peer_bi->num_freq; i++) { ++ freq = peer_bi->freq[i]; ++ if (freq_included(auth->freq, auth->num_freq, freq)) ++ continue; ++ if (dpp_channel_ok_init(own_modes, num_modes, freq)) ++ auth->freq[auth->num_freq++] = freq; ++ } ++ if (!auth->num_freq) { ++ wpa_printf(MSG_INFO, ++ "DPP: No available channels for initiating DPP Authentication"); ++ return -1; ++ } ++ auth->curr_freq = auth->freq[0]; ++ return 0; ++} ++ ++ ++static int dpp_channel_local_list(struct dpp_authentication *auth, ++ struct hostapd_hw_modes *own_modes, ++ u16 num_modes) ++{ ++ u16 m; ++ int c, flag; ++ unsigned int freq; ++ ++ auth->num_freq = 0; ++ ++ if (!own_modes || !num_modes) { ++ auth->freq[0] = 2412; ++ auth->freq[1] = 2437; ++ auth->freq[2] = 2462; ++ auth->num_freq = 3; ++ return 0; ++ } ++ ++ for (m = 0; m < num_modes; m++) { ++ for (c = 0; c < own_modes[m].num_channels; c++) { ++ freq = own_modes[m].channels[c].freq; ++ flag = own_modes[m].channels[c].flag; ++ if (flag & (HOSTAPD_CHAN_DISABLED | ++ HOSTAPD_CHAN_NO_IR | ++ HOSTAPD_CHAN_RADAR)) ++ continue; ++ if (freq_included(auth->freq, auth->num_freq, freq)) ++ continue; ++ auth->freq[auth->num_freq++] = freq; ++ if (auth->num_freq == DPP_BOOTSTRAP_MAX_FREQ) { ++ m = num_modes; ++ break; ++ } ++ } ++ } ++ ++ return auth->num_freq == 0 ? -1 : 0; ++} ++ ++ ++static int dpp_prepare_channel_list(struct dpp_authentication *auth, ++ struct hostapd_hw_modes *own_modes, ++ u16 num_modes) ++{ ++ int res; ++ char freqs[DPP_BOOTSTRAP_MAX_FREQ * 6 + 10], *pos, *end; ++ unsigned int i; ++ ++ if (auth->peer_bi->num_freq > 0) ++ res = dpp_channel_intersect(auth, own_modes, num_modes); ++ else ++ res = dpp_channel_local_list(auth, own_modes, num_modes); ++ if (res < 0) ++ return res; ++ ++ /* Prioritize 2.4 GHz channels 6, 1, 11 (in this order) to hit the most ++ * likely channels first. */ ++ freq_to_start(auth->freq, auth->num_freq, 2462); ++ freq_to_start(auth->freq, auth->num_freq, 2412); ++ freq_to_start(auth->freq, auth->num_freq, 2437); ++ ++ auth->freq_idx = 0; ++ auth->curr_freq = auth->freq[0]; ++ ++ pos = freqs; ++ end = pos + sizeof(freqs); ++ for (i = 0; i < auth->num_freq; i++) { ++ res = os_snprintf(pos, end - pos, " %u", auth->freq[i]); ++ if (os_snprintf_error(end - pos, res)) ++ break; ++ pos += res; ++ } ++ *pos = '\0'; ++ wpa_printf(MSG_DEBUG, "DPP: Possible frequencies for initiating:%s", ++ freqs); ++ ++ return 0; ++} ++ ++ ++static int dpp_autogen_bootstrap_key(struct dpp_authentication *auth) ++{ ++ struct dpp_bootstrap_info *bi; ++ char *pk = NULL; ++ size_t len; ++ ++ if (auth->own_bi) ++ return 0; /* already generated */ ++ ++ bi = os_zalloc(sizeof(*bi)); ++ if (!bi) ++ return -1; ++ bi->type = DPP_BOOTSTRAP_QR_CODE; ++ pk = dpp_keygen(bi, auth->peer_bi->curve->name, NULL, 0); ++ if (!pk) ++ goto fail; ++ ++ len = 4; /* "DPP:" */ ++ len += 4 + os_strlen(pk); ++ bi->uri = os_malloc(len + 1); ++ if (!bi->uri) ++ goto fail; ++ os_snprintf(bi->uri, len + 1, "DPP:K:%s;;", pk); ++ wpa_printf(MSG_DEBUG, ++ "DPP: Auto-generated own bootstrapping key info: URI %s", ++ bi->uri); ++ ++ auth->tmp_own_bi = auth->own_bi = bi; ++ ++ os_free(pk); ++ ++ return 0; ++fail: ++ os_free(pk); ++ dpp_bootstrap_info_free(bi); ++ return -1; ++} ++ ++ ++struct dpp_authentication * dpp_auth_init(void *msg_ctx, ++ struct dpp_bootstrap_info *peer_bi, ++ struct dpp_bootstrap_info *own_bi, ++ u8 dpp_allowed_roles, ++ unsigned int neg_freq, ++ struct hostapd_hw_modes *own_modes, ++ u16 num_modes) ++{ ++ struct dpp_authentication *auth; ++ size_t nonce_len; ++ EVP_PKEY_CTX *ctx = NULL; ++ size_t secret_len; ++ struct wpabuf *pi = NULL; ++ const u8 *r_pubkey_hash, *i_pubkey_hash; ++#ifdef CONFIG_TESTING_OPTIONS ++ u8 test_hash[SHA256_MAC_LEN]; ++#endif /* CONFIG_TESTING_OPTIONS */ ++ ++ auth = os_zalloc(sizeof(*auth)); ++ if (!auth) ++ return NULL; ++ auth->msg_ctx = msg_ctx; ++ auth->initiator = 1; ++ auth->waiting_auth_resp = 1; ++ auth->allowed_roles = dpp_allowed_roles; ++ auth->configurator = !!(dpp_allowed_roles & DPP_CAPAB_CONFIGURATOR); ++ auth->peer_bi = peer_bi; ++ auth->own_bi = own_bi; ++ auth->curve = peer_bi->curve; ++ ++ if (dpp_autogen_bootstrap_key(auth) < 0 || ++ dpp_prepare_channel_list(auth, own_modes, num_modes) < 0) ++ goto fail; ++ ++#ifdef CONFIG_TESTING_OPTIONS ++ if (dpp_nonce_override_len > 0) { ++ wpa_printf(MSG_INFO, "DPP: TESTING - override I-nonce"); ++ nonce_len = dpp_nonce_override_len; ++ os_memcpy(auth->i_nonce, dpp_nonce_override, nonce_len); ++ } else { ++ nonce_len = auth->curve->nonce_len; ++ if (random_get_bytes(auth->i_nonce, nonce_len)) { ++ wpa_printf(MSG_ERROR, ++ "DPP: Failed to generate I-nonce"); ++ goto fail; ++ } ++ } ++#else /* CONFIG_TESTING_OPTIONS */ ++ nonce_len = auth->curve->nonce_len; ++ if (random_get_bytes(auth->i_nonce, nonce_len)) { ++ wpa_printf(MSG_ERROR, "DPP: Failed to generate I-nonce"); ++ goto fail; ++ } ++#endif /* CONFIG_TESTING_OPTIONS */ ++ wpa_hexdump(MSG_DEBUG, "DPP: I-nonce", auth->i_nonce, nonce_len); ++ ++#ifdef CONFIG_TESTING_OPTIONS ++ if (dpp_protocol_key_override_len) { ++ const struct dpp_curve_params *tmp_curve; ++ ++ wpa_printf(MSG_INFO, ++ "DPP: TESTING - override protocol key"); ++ auth->own_protocol_key = dpp_set_keypair( ++ &tmp_curve, dpp_protocol_key_override, ++ dpp_protocol_key_override_len); ++ } else { ++ auth->own_protocol_key = dpp_gen_keypair(auth->curve); ++ } ++#else /* CONFIG_TESTING_OPTIONS */ ++ auth->own_protocol_key = dpp_gen_keypair(auth->curve); ++#endif /* CONFIG_TESTING_OPTIONS */ ++ if (!auth->own_protocol_key) ++ goto fail; ++ ++ pi = dpp_get_pubkey_point(auth->own_protocol_key, 0); ++ if (!pi) ++ goto fail; ++ ++ /* ECDH: M = pI * BR */ ++ ctx = EVP_PKEY_CTX_new(auth->own_protocol_key, NULL); ++ if (!ctx || ++ EVP_PKEY_derive_init(ctx) != 1 || ++ EVP_PKEY_derive_set_peer(ctx, auth->peer_bi->pubkey) != 1 || ++ EVP_PKEY_derive(ctx, NULL, &secret_len) != 1 || ++ secret_len > DPP_MAX_SHARED_SECRET_LEN || ++ EVP_PKEY_derive(ctx, auth->Mx, &secret_len) != 1) { ++ wpa_printf(MSG_ERROR, ++ "DPP: Failed to derive ECDH shared secret: %s", ++ ERR_error_string(ERR_get_error(), NULL)); ++ goto fail; ++ } ++ auth->secret_len = secret_len; ++ EVP_PKEY_CTX_free(ctx); ++ ctx = NULL; ++ ++ wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (M.x)", ++ auth->Mx, auth->secret_len); ++ auth->Mx_len = auth->secret_len; ++ ++ if (dpp_derive_k1(auth->Mx, auth->secret_len, auth->k1, ++ auth->curve->hash_len) < 0) ++ goto fail; ++ ++ r_pubkey_hash = auth->peer_bi->pubkey_hash; ++ i_pubkey_hash = auth->own_bi->pubkey_hash; ++ ++#ifdef CONFIG_TESTING_OPTIONS ++ if (dpp_test == DPP_TEST_NO_R_BOOTSTRAP_KEY_HASH_AUTH_REQ) { ++ wpa_printf(MSG_INFO, "DPP: TESTING - no R-Bootstrap Key Hash"); ++ r_pubkey_hash = NULL; ++ } else if (dpp_test == DPP_TEST_INVALID_R_BOOTSTRAP_KEY_HASH_AUTH_REQ) { ++ wpa_printf(MSG_INFO, ++ "DPP: TESTING - invalid R-Bootstrap Key Hash"); ++ os_memcpy(test_hash, r_pubkey_hash, SHA256_MAC_LEN); ++ test_hash[SHA256_MAC_LEN - 1] ^= 0x01; ++ r_pubkey_hash = test_hash; ++ } else if (dpp_test == DPP_TEST_NO_I_BOOTSTRAP_KEY_HASH_AUTH_REQ) { ++ wpa_printf(MSG_INFO, "DPP: TESTING - no I-Bootstrap Key Hash"); ++ i_pubkey_hash = NULL; ++ } else if (dpp_test == DPP_TEST_INVALID_I_BOOTSTRAP_KEY_HASH_AUTH_REQ) { ++ wpa_printf(MSG_INFO, ++ "DPP: TESTING - invalid I-Bootstrap Key Hash"); ++ os_memcpy(test_hash, i_pubkey_hash, SHA256_MAC_LEN); ++ test_hash[SHA256_MAC_LEN - 1] ^= 0x01; ++ i_pubkey_hash = test_hash; ++ } else if (dpp_test == DPP_TEST_NO_I_PROTO_KEY_AUTH_REQ) { ++ wpa_printf(MSG_INFO, "DPP: TESTING - no I-Proto Key"); ++ wpabuf_free(pi); ++ pi = NULL; ++ } else if (dpp_test == DPP_TEST_INVALID_I_PROTO_KEY_AUTH_REQ) { ++ wpa_printf(MSG_INFO, "DPP: TESTING - invalid I-Proto Key"); ++ wpabuf_free(pi); ++ pi = wpabuf_alloc(2 * auth->curve->prime_len); ++ if (!pi || dpp_test_gen_invalid_key(pi, auth->curve) < 0) ++ goto fail; ++ } ++#endif /* CONFIG_TESTING_OPTIONS */ ++ ++ auth->req_msg = dpp_auth_build_req(auth, pi, nonce_len, r_pubkey_hash, ++ i_pubkey_hash, neg_freq); ++ if (!auth->req_msg) ++ goto fail; ++ ++out: ++ wpabuf_free(pi); ++ EVP_PKEY_CTX_free(ctx); ++ return auth; ++fail: ++ dpp_auth_deinit(auth); ++ auth = NULL; ++ goto out; ++} ++ ++ ++static struct wpabuf * dpp_build_conf_req_attr(struct dpp_authentication *auth, ++ const char *json) ++{ ++ size_t nonce_len; ++ size_t json_len, clear_len; ++ struct wpabuf *clear = NULL, *msg = NULL; ++ u8 *wrapped; ++ size_t attr_len; ++ ++ wpa_printf(MSG_DEBUG, "DPP: Build configuration request"); ++ ++ nonce_len = auth->curve->nonce_len; ++ if (random_get_bytes(auth->e_nonce, nonce_len)) { ++ wpa_printf(MSG_ERROR, "DPP: Failed to generate E-nonce"); ++ goto fail; ++ } ++ wpa_hexdump(MSG_DEBUG, "DPP: E-nonce", auth->e_nonce, nonce_len); ++ json_len = os_strlen(json); ++ wpa_hexdump_ascii(MSG_DEBUG, "DPP: configAttr JSON", json, json_len); ++ ++ /* { E-nonce, configAttrib }ke */ ++ clear_len = 4 + nonce_len + 4 + json_len; ++ clear = wpabuf_alloc(clear_len); ++ attr_len = 4 + clear_len + AES_BLOCK_SIZE; ++#ifdef CONFIG_TESTING_OPTIONS ++ if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_CONF_REQ) ++ attr_len += 5; ++#endif /* CONFIG_TESTING_OPTIONS */ ++ msg = wpabuf_alloc(attr_len); ++ if (!clear || !msg) ++ goto fail; ++ ++#ifdef CONFIG_TESTING_OPTIONS ++ if (dpp_test == DPP_TEST_NO_E_NONCE_CONF_REQ) { ++ wpa_printf(MSG_INFO, "DPP: TESTING - no E-nonce"); ++ goto skip_e_nonce; ++ } ++ if (dpp_test == DPP_TEST_INVALID_E_NONCE_CONF_REQ) { ++ wpa_printf(MSG_INFO, "DPP: TESTING - invalid E-nonce"); ++ wpabuf_put_le16(clear, DPP_ATTR_ENROLLEE_NONCE); ++ wpabuf_put_le16(clear, nonce_len - 1); ++ wpabuf_put_data(clear, auth->e_nonce, nonce_len - 1); ++ goto skip_e_nonce; ++ } ++ if (dpp_test == DPP_TEST_NO_WRAPPED_DATA_CONF_REQ) { ++ wpa_printf(MSG_INFO, "DPP: TESTING - no Wrapped Data"); ++ goto skip_wrapped_data; ++ } ++#endif /* CONFIG_TESTING_OPTIONS */ ++ ++ /* E-nonce */ ++ wpabuf_put_le16(clear, DPP_ATTR_ENROLLEE_NONCE); ++ wpabuf_put_le16(clear, nonce_len); ++ wpabuf_put_data(clear, auth->e_nonce, nonce_len); ++ ++#ifdef CONFIG_TESTING_OPTIONS ++skip_e_nonce: ++ if (dpp_test == DPP_TEST_NO_CONFIG_ATTR_OBJ_CONF_REQ) { ++ wpa_printf(MSG_INFO, "DPP: TESTING - no configAttrib"); ++ goto skip_conf_attr_obj; ++ } ++#endif /* CONFIG_TESTING_OPTIONS */ ++ ++ /* configAttrib */ ++ wpabuf_put_le16(clear, DPP_ATTR_CONFIG_ATTR_OBJ); ++ wpabuf_put_le16(clear, json_len); ++ wpabuf_put_data(clear, json, json_len); ++ ++#ifdef CONFIG_TESTING_OPTIONS ++skip_conf_attr_obj: ++#endif /* CONFIG_TESTING_OPTIONS */ ++ ++ wpabuf_put_le16(msg, DPP_ATTR_WRAPPED_DATA); ++ wpabuf_put_le16(msg, wpabuf_len(clear) + AES_BLOCK_SIZE); ++ wrapped = wpabuf_put(msg, wpabuf_len(clear) + AES_BLOCK_SIZE); ++ ++ /* No AES-SIV AD */ ++ wpa_hexdump_buf(MSG_DEBUG, "DPP: AES-SIV cleartext", clear); ++ if (aes_siv_encrypt(auth->ke, auth->curve->hash_len, ++ wpabuf_head(clear), wpabuf_len(clear), ++ 0, NULL, NULL, wrapped) < 0) ++ goto fail; ++ wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext", ++ wrapped, wpabuf_len(clear) + AES_BLOCK_SIZE); ++ ++#ifdef CONFIG_TESTING_OPTIONS ++ if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_CONF_REQ) { ++ wpa_printf(MSG_INFO, "DPP: TESTING - attr after Wrapped Data"); ++ dpp_build_attr_status(msg, DPP_STATUS_OK); ++ } ++skip_wrapped_data: ++#endif /* CONFIG_TESTING_OPTIONS */ ++ ++ wpa_hexdump_buf(MSG_DEBUG, ++ "DPP: Configuration Request frame attributes", msg); ++ wpabuf_free(clear); ++ return msg; ++ ++fail: ++ wpabuf_free(clear); ++ wpabuf_free(msg); ++ return NULL; ++} ++ ++ ++static void dpp_write_adv_proto(struct wpabuf *buf) ++{ ++ /* Advertisement Protocol IE */ ++ wpabuf_put_u8(buf, WLAN_EID_ADV_PROTO); ++ wpabuf_put_u8(buf, 8); /* Length */ ++ wpabuf_put_u8(buf, 0x7f); ++ wpabuf_put_u8(buf, WLAN_EID_VENDOR_SPECIFIC); ++ wpabuf_put_u8(buf, 5); ++ wpabuf_put_be24(buf, OUI_WFA); ++ wpabuf_put_u8(buf, DPP_OUI_TYPE); ++ wpabuf_put_u8(buf, 0x01); ++} ++ ++ ++static void dpp_write_gas_query(struct wpabuf *buf, struct wpabuf *query) ++{ ++ /* GAS Query */ ++ wpabuf_put_le16(buf, wpabuf_len(query)); ++ wpabuf_put_buf(buf, query); ++} ++ ++ ++struct wpabuf * dpp_build_conf_req(struct dpp_authentication *auth, ++ const char *json) ++{ ++ struct wpabuf *buf, *conf_req; ++ ++ conf_req = dpp_build_conf_req_attr(auth, json); ++ if (!conf_req) { ++ wpa_printf(MSG_DEBUG, ++ "DPP: No configuration request data available"); ++ return NULL; ++ } ++ ++ buf = gas_build_initial_req(0, 10 + 2 + wpabuf_len(conf_req)); ++ if (!buf) { ++ wpabuf_free(conf_req); ++ return NULL; ++ } ++ ++ dpp_write_adv_proto(buf); ++ dpp_write_gas_query(buf, conf_req); ++ wpabuf_free(conf_req); ++ wpa_hexdump_buf(MSG_MSGDUMP, "DPP: GAS Config Request", buf); ++ ++ return buf; ++} ++ ++ ++static void dpp_auth_success(struct dpp_authentication *auth) ++{ ++ wpa_printf(MSG_DEBUG, ++ "DPP: Authentication success - clear temporary keys"); ++ os_memset(auth->Mx, 0, sizeof(auth->Mx)); ++ auth->Mx_len = 0; ++ os_memset(auth->Nx, 0, sizeof(auth->Nx)); ++ auth->Nx_len = 0; ++ os_memset(auth->Lx, 0, sizeof(auth->Lx)); ++ auth->Lx_len = 0; ++ os_memset(auth->k1, 0, sizeof(auth->k1)); ++ os_memset(auth->k2, 0, sizeof(auth->k2)); ++ ++ auth->auth_success = 1; ++} ++ ++ ++static int dpp_gen_r_auth(struct dpp_authentication *auth, u8 *r_auth) ++{ ++ struct wpabuf *pix, *prx, *bix, *brx; ++ const u8 *addr[7]; ++ size_t len[7]; ++ size_t i, num_elem = 0; ++ size_t nonce_len; ++ u8 zero = 0; ++ int res = -1; ++ ++ /* R-auth = H(I-nonce | R-nonce | PI.x | PR.x | [BI.x |] BR.x | 0) */ ++ nonce_len = auth->curve->nonce_len; ++ ++ if (auth->initiator) { ++ pix = dpp_get_pubkey_point(auth->own_protocol_key, 0); ++ prx = dpp_get_pubkey_point(auth->peer_protocol_key, 0); ++ if (auth->own_bi) ++ bix = dpp_get_pubkey_point(auth->own_bi->pubkey, 0); ++ else ++ bix = NULL; ++ brx = dpp_get_pubkey_point(auth->peer_bi->pubkey, 0); ++ } else { ++ pix = dpp_get_pubkey_point(auth->peer_protocol_key, 0); ++ prx = dpp_get_pubkey_point(auth->own_protocol_key, 0); ++ if (auth->peer_bi) ++ bix = dpp_get_pubkey_point(auth->peer_bi->pubkey, 0); ++ else ++ bix = NULL; ++ brx = dpp_get_pubkey_point(auth->own_bi->pubkey, 0); ++ } ++ if (!pix || !prx || !brx) ++ goto fail; ++ ++ addr[num_elem] = auth->i_nonce; ++ len[num_elem] = nonce_len; ++ num_elem++; ++ ++ addr[num_elem] = auth->r_nonce; ++ len[num_elem] = nonce_len; ++ num_elem++; ++ ++ addr[num_elem] = wpabuf_head(pix); ++ len[num_elem] = wpabuf_len(pix) / 2; ++ num_elem++; ++ ++ addr[num_elem] = wpabuf_head(prx); ++ len[num_elem] = wpabuf_len(prx) / 2; ++ num_elem++; ++ ++ if (bix) { ++ addr[num_elem] = wpabuf_head(bix); ++ len[num_elem] = wpabuf_len(bix) / 2; ++ num_elem++; ++ } ++ ++ addr[num_elem] = wpabuf_head(brx); ++ len[num_elem] = wpabuf_len(brx) / 2; ++ num_elem++; ++ ++ addr[num_elem] = &zero; ++ len[num_elem] = 1; ++ num_elem++; ++ ++ wpa_printf(MSG_DEBUG, "DPP: R-auth hash components"); ++ for (i = 0; i < num_elem; i++) ++ wpa_hexdump(MSG_DEBUG, "DPP: hash component", addr[i], len[i]); ++ res = dpp_hash_vector(auth->curve, num_elem, addr, len, r_auth); ++ if (res == 0) ++ wpa_hexdump(MSG_DEBUG, "DPP: R-auth", r_auth, ++ auth->curve->hash_len); ++fail: ++ wpabuf_free(pix); ++ wpabuf_free(prx); ++ wpabuf_free(bix); ++ wpabuf_free(brx); ++ return res; ++} ++ ++ ++static int dpp_gen_i_auth(struct dpp_authentication *auth, u8 *i_auth) ++{ ++ struct wpabuf *pix = NULL, *prx = NULL, *bix = NULL, *brx = NULL; ++ const u8 *addr[7]; ++ size_t len[7]; ++ size_t i, num_elem = 0; ++ size_t nonce_len; ++ u8 one = 1; ++ int res = -1; ++ ++ /* I-auth = H(R-nonce | I-nonce | PR.x | PI.x | BR.x | [BI.x |] 1) */ ++ nonce_len = auth->curve->nonce_len; ++ ++ if (auth->initiator) { ++ pix = dpp_get_pubkey_point(auth->own_protocol_key, 0); ++ prx = dpp_get_pubkey_point(auth->peer_protocol_key, 0); ++ if (auth->own_bi) ++ bix = dpp_get_pubkey_point(auth->own_bi->pubkey, 0); ++ else ++ bix = NULL; ++ if (!auth->peer_bi) ++ goto fail; ++ brx = dpp_get_pubkey_point(auth->peer_bi->pubkey, 0); ++ } else { ++ pix = dpp_get_pubkey_point(auth->peer_protocol_key, 0); ++ prx = dpp_get_pubkey_point(auth->own_protocol_key, 0); ++ if (auth->peer_bi) ++ bix = dpp_get_pubkey_point(auth->peer_bi->pubkey, 0); ++ else ++ bix = NULL; ++ if (!auth->own_bi) ++ goto fail; ++ brx = dpp_get_pubkey_point(auth->own_bi->pubkey, 0); ++ } ++ if (!pix || !prx || !brx) ++ goto fail; ++ ++ addr[num_elem] = auth->r_nonce; ++ len[num_elem] = nonce_len; ++ num_elem++; ++ ++ addr[num_elem] = auth->i_nonce; ++ len[num_elem] = nonce_len; ++ num_elem++; ++ ++ addr[num_elem] = wpabuf_head(prx); ++ len[num_elem] = wpabuf_len(prx) / 2; ++ num_elem++; ++ ++ addr[num_elem] = wpabuf_head(pix); ++ len[num_elem] = wpabuf_len(pix) / 2; ++ num_elem++; ++ ++ addr[num_elem] = wpabuf_head(brx); ++ len[num_elem] = wpabuf_len(brx) / 2; ++ num_elem++; ++ ++ if (bix) { ++ addr[num_elem] = wpabuf_head(bix); ++ len[num_elem] = wpabuf_len(bix) / 2; ++ num_elem++; ++ } ++ ++ addr[num_elem] = &one; ++ len[num_elem] = 1; ++ num_elem++; ++ ++ wpa_printf(MSG_DEBUG, "DPP: I-auth hash components"); ++ for (i = 0; i < num_elem; i++) ++ wpa_hexdump(MSG_DEBUG, "DPP: hash component", addr[i], len[i]); ++ res = dpp_hash_vector(auth->curve, num_elem, addr, len, i_auth); ++ if (res == 0) ++ wpa_hexdump(MSG_DEBUG, "DPP: I-auth", i_auth, ++ auth->curve->hash_len); ++fail: ++ wpabuf_free(pix); ++ wpabuf_free(prx); ++ wpabuf_free(bix); ++ wpabuf_free(brx); ++ return res; ++} ++ ++ ++static int dpp_auth_derive_l_responder(struct dpp_authentication *auth) ++{ ++ const EC_GROUP *group; ++ EC_POINT *l = NULL; ++ EC_KEY *BI = NULL, *bR = NULL, *pR = NULL; ++ const EC_POINT *BI_point; ++ BN_CTX *bnctx; ++ BIGNUM *lx, *sum, *q; ++ const BIGNUM *bR_bn, *pR_bn; ++ int ret = -1; ++ ++ /* L = ((bR + pR) modulo q) * BI */ ++ ++ bnctx = BN_CTX_new(); ++ sum = BN_new(); ++ q = BN_new(); ++ lx = BN_new(); ++ if (!bnctx || !sum || !q || !lx) ++ goto fail; ++ BI = EVP_PKEY_get1_EC_KEY(auth->peer_bi->pubkey); ++ if (!BI) ++ goto fail; ++ BI_point = EC_KEY_get0_public_key(BI); ++ group = EC_KEY_get0_group(BI); ++ if (!group) ++ goto fail; ++ ++ bR = EVP_PKEY_get1_EC_KEY(auth->own_bi->pubkey); ++ pR = EVP_PKEY_get1_EC_KEY(auth->own_protocol_key); ++ if (!bR || !pR) ++ goto fail; ++ bR_bn = EC_KEY_get0_private_key(bR); ++ pR_bn = EC_KEY_get0_private_key(pR); ++ if (!bR_bn || !pR_bn) ++ goto fail; ++ if (EC_GROUP_get_order(group, q, bnctx) != 1 || ++ BN_mod_add(sum, bR_bn, pR_bn, q, bnctx) != 1) ++ goto fail; ++ l = EC_POINT_new(group); ++ if (!l || ++ EC_POINT_mul(group, l, NULL, BI_point, sum, bnctx) != 1 || ++ EC_POINT_get_affine_coordinates_GFp(group, l, lx, NULL, ++ bnctx) != 1) { ++ wpa_printf(MSG_ERROR, ++ "OpenSSL: failed: %s", ++ ERR_error_string(ERR_get_error(), NULL)); ++ goto fail; ++ } ++ ++ if (dpp_bn2bin_pad(lx, auth->Lx, auth->secret_len) < 0) ++ goto fail; ++ wpa_hexdump_key(MSG_DEBUG, "DPP: L.x", auth->Lx, auth->secret_len); ++ auth->Lx_len = auth->secret_len; ++ ret = 0; ++fail: ++ EC_POINT_clear_free(l); ++ EC_KEY_free(BI); ++ EC_KEY_free(bR); ++ EC_KEY_free(pR); ++ BN_clear_free(lx); ++ BN_clear_free(sum); ++ BN_free(q); ++ BN_CTX_free(bnctx); ++ return ret; ++} ++ ++ ++static int dpp_auth_derive_l_initiator(struct dpp_authentication *auth) ++{ ++ const EC_GROUP *group; ++ EC_POINT *l = NULL, *sum = NULL; ++ EC_KEY *bI = NULL, *BR = NULL, *PR = NULL; ++ const EC_POINT *BR_point, *PR_point; ++ BN_CTX *bnctx; ++ BIGNUM *lx; ++ const BIGNUM *bI_bn; ++ int ret = -1; ++ ++ /* L = bI * (BR + PR) */ ++ ++ bnctx = BN_CTX_new(); ++ lx = BN_new(); ++ if (!bnctx || !lx) ++ goto fail; ++ BR = EVP_PKEY_get1_EC_KEY(auth->peer_bi->pubkey); ++ PR = EVP_PKEY_get1_EC_KEY(auth->peer_protocol_key); ++ if (!BR || !PR) ++ goto fail; ++ BR_point = EC_KEY_get0_public_key(BR); ++ PR_point = EC_KEY_get0_public_key(PR); ++ ++ bI = EVP_PKEY_get1_EC_KEY(auth->own_bi->pubkey); ++ if (!bI) ++ goto fail; ++ group = EC_KEY_get0_group(bI); ++ bI_bn = EC_KEY_get0_private_key(bI); ++ if (!group || !bI_bn) ++ goto fail; ++ sum = EC_POINT_new(group); ++ l = EC_POINT_new(group); ++ if (!sum || !l || ++ EC_POINT_add(group, sum, BR_point, PR_point, bnctx) != 1 || ++ EC_POINT_mul(group, l, NULL, sum, bI_bn, bnctx) != 1 || ++ EC_POINT_get_affine_coordinates_GFp(group, l, lx, NULL, ++ bnctx) != 1) { ++ wpa_printf(MSG_ERROR, ++ "OpenSSL: failed: %s", ++ ERR_error_string(ERR_get_error(), NULL)); ++ goto fail; ++ } ++ ++ if (dpp_bn2bin_pad(lx, auth->Lx, auth->secret_len) < 0) ++ goto fail; ++ wpa_hexdump_key(MSG_DEBUG, "DPP: L.x", auth->Lx, auth->secret_len); ++ auth->Lx_len = auth->secret_len; ++ ret = 0; ++fail: ++ EC_POINT_clear_free(l); ++ EC_POINT_clear_free(sum); ++ EC_KEY_free(bI); ++ EC_KEY_free(BR); ++ EC_KEY_free(PR); ++ BN_clear_free(lx); ++ BN_CTX_free(bnctx); ++ return ret; ++} ++ ++ ++static int dpp_auth_build_resp_ok(struct dpp_authentication *auth) ++{ ++ size_t nonce_len; ++ EVP_PKEY_CTX *ctx = NULL; ++ size_t secret_len; ++ struct wpabuf *msg, *pr = NULL; ++ u8 r_auth[4 + DPP_MAX_HASH_LEN]; ++ u8 wrapped_r_auth[4 + DPP_MAX_HASH_LEN + AES_BLOCK_SIZE], *w_r_auth; ++ size_t wrapped_r_auth_len; ++ int ret = -1; ++ const u8 *r_pubkey_hash, *i_pubkey_hash, *r_nonce, *i_nonce; ++ enum dpp_status_error status = DPP_STATUS_OK; ++#ifdef CONFIG_TESTING_OPTIONS ++ u8 test_hash[SHA256_MAC_LEN]; ++#endif /* CONFIG_TESTING_OPTIONS */ ++ ++ wpa_printf(MSG_DEBUG, "DPP: Build Authentication Response"); ++ if (!auth->own_bi) ++ return -1; ++ ++#ifdef CONFIG_TESTING_OPTIONS ++ if (dpp_nonce_override_len > 0) { ++ wpa_printf(MSG_INFO, "DPP: TESTING - override R-nonce"); ++ nonce_len = dpp_nonce_override_len; ++ os_memcpy(auth->r_nonce, dpp_nonce_override, nonce_len); ++ } else { ++ nonce_len = auth->curve->nonce_len; ++ if (random_get_bytes(auth->r_nonce, nonce_len)) { ++ wpa_printf(MSG_ERROR, ++ "DPP: Failed to generate R-nonce"); ++ goto fail; ++ } ++ } ++#else /* CONFIG_TESTING_OPTIONS */ ++ nonce_len = auth->curve->nonce_len; ++ if (random_get_bytes(auth->r_nonce, nonce_len)) { ++ wpa_printf(MSG_ERROR, "DPP: Failed to generate R-nonce"); ++ goto fail; ++ } ++#endif /* CONFIG_TESTING_OPTIONS */ ++ wpa_hexdump(MSG_DEBUG, "DPP: R-nonce", auth->r_nonce, nonce_len); ++ ++#ifdef CONFIG_TESTING_OPTIONS ++ if (dpp_protocol_key_override_len) { ++ const struct dpp_curve_params *tmp_curve; ++ ++ wpa_printf(MSG_INFO, ++ "DPP: TESTING - override protocol key"); ++ auth->own_protocol_key = dpp_set_keypair( ++ &tmp_curve, dpp_protocol_key_override, ++ dpp_protocol_key_override_len); ++ } else { ++ auth->own_protocol_key = dpp_gen_keypair(auth->curve); ++ } ++#else /* CONFIG_TESTING_OPTIONS */ ++ auth->own_protocol_key = dpp_gen_keypair(auth->curve); ++#endif /* CONFIG_TESTING_OPTIONS */ ++ if (!auth->own_protocol_key) ++ goto fail; ++ ++ pr = dpp_get_pubkey_point(auth->own_protocol_key, 0); ++ if (!pr) ++ goto fail; ++ ++ /* ECDH: N = pR * PI */ ++ ctx = EVP_PKEY_CTX_new(auth->own_protocol_key, NULL); ++ if (!ctx || ++ EVP_PKEY_derive_init(ctx) != 1 || ++ EVP_PKEY_derive_set_peer(ctx, auth->peer_protocol_key) != 1 || ++ EVP_PKEY_derive(ctx, NULL, &secret_len) != 1 || ++ secret_len > DPP_MAX_SHARED_SECRET_LEN || ++ EVP_PKEY_derive(ctx, auth->Nx, &secret_len) != 1) { ++ wpa_printf(MSG_ERROR, ++ "DPP: Failed to derive ECDH shared secret: %s", ++ ERR_error_string(ERR_get_error(), NULL)); ++ goto fail; ++ } ++ EVP_PKEY_CTX_free(ctx); ++ ctx = NULL; ++ ++ wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (N.x)", ++ auth->Nx, auth->secret_len); ++ auth->Nx_len = auth->secret_len; ++ ++ if (dpp_derive_k2(auth->Nx, auth->secret_len, auth->k2, ++ auth->curve->hash_len) < 0) ++ goto fail; ++ ++ if (auth->own_bi && auth->peer_bi) { ++ /* Mutual authentication */ ++ if (dpp_auth_derive_l_responder(auth) < 0) ++ goto fail; ++ } ++ ++ if (dpp_derive_ke(auth, auth->ke, auth->curve->hash_len) < 0) ++ goto fail; ++ ++ /* R-auth = H(I-nonce | R-nonce | PI.x | PR.x | [BI.x |] BR.x | 0) */ ++ WPA_PUT_LE16(r_auth, DPP_ATTR_R_AUTH_TAG); ++ WPA_PUT_LE16(&r_auth[2], auth->curve->hash_len); ++ if (dpp_gen_r_auth(auth, r_auth + 4) < 0) ++ goto fail; ++#ifdef CONFIG_TESTING_OPTIONS ++ if (dpp_test == DPP_TEST_R_AUTH_MISMATCH_AUTH_RESP) { ++ wpa_printf(MSG_INFO, "DPP: TESTING - R-auth mismatch"); ++ r_auth[4 + auth->curve->hash_len / 2] ^= 0x01; ++ } ++#endif /* CONFIG_TESTING_OPTIONS */ ++ if (aes_siv_encrypt(auth->ke, auth->curve->hash_len, ++ r_auth, 4 + auth->curve->hash_len, ++ 0, NULL, NULL, wrapped_r_auth) < 0) ++ goto fail; ++ wrapped_r_auth_len = 4 + auth->curve->hash_len + AES_BLOCK_SIZE; ++ wpa_hexdump(MSG_DEBUG, "DPP: {R-auth}ke", ++ wrapped_r_auth, wrapped_r_auth_len); ++ w_r_auth = wrapped_r_auth; ++ ++ r_pubkey_hash = auth->own_bi->pubkey_hash; ++ if (auth->peer_bi) ++ i_pubkey_hash = auth->peer_bi->pubkey_hash; ++ else ++ i_pubkey_hash = NULL; ++ ++ i_nonce = auth->i_nonce; ++ r_nonce = auth->r_nonce; ++ ++#ifdef CONFIG_TESTING_OPTIONS ++ if (dpp_test == DPP_TEST_NO_R_BOOTSTRAP_KEY_HASH_AUTH_RESP) { ++ wpa_printf(MSG_INFO, "DPP: TESTING - no R-Bootstrap Key Hash"); ++ r_pubkey_hash = NULL; ++ } else if (dpp_test == ++ DPP_TEST_INVALID_R_BOOTSTRAP_KEY_HASH_AUTH_RESP) { ++ wpa_printf(MSG_INFO, ++ "DPP: TESTING - invalid R-Bootstrap Key Hash"); ++ os_memcpy(test_hash, r_pubkey_hash, SHA256_MAC_LEN); ++ test_hash[SHA256_MAC_LEN - 1] ^= 0x01; ++ r_pubkey_hash = test_hash; ++ } else if (dpp_test == DPP_TEST_NO_I_BOOTSTRAP_KEY_HASH_AUTH_RESP) { ++ wpa_printf(MSG_INFO, "DPP: TESTING - no I-Bootstrap Key Hash"); ++ i_pubkey_hash = NULL; ++ } else if (dpp_test == ++ DPP_TEST_INVALID_I_BOOTSTRAP_KEY_HASH_AUTH_RESP) { ++ wpa_printf(MSG_INFO, ++ "DPP: TESTING - invalid I-Bootstrap Key Hash"); ++ if (i_pubkey_hash) ++ os_memcpy(test_hash, i_pubkey_hash, SHA256_MAC_LEN); ++ else ++ os_memset(test_hash, 0, SHA256_MAC_LEN); ++ test_hash[SHA256_MAC_LEN - 1] ^= 0x01; ++ i_pubkey_hash = test_hash; ++ } else if (dpp_test == DPP_TEST_NO_R_PROTO_KEY_AUTH_RESP) { ++ wpa_printf(MSG_INFO, "DPP: TESTING - no R-Proto Key"); ++ wpabuf_free(pr); ++ pr = NULL; ++ } else if (dpp_test == DPP_TEST_INVALID_R_PROTO_KEY_AUTH_RESP) { ++ wpa_printf(MSG_INFO, "DPP: TESTING - invalid R-Proto Key"); ++ wpabuf_free(pr); ++ pr = wpabuf_alloc(2 * auth->curve->prime_len); ++ if (!pr || dpp_test_gen_invalid_key(pr, auth->curve) < 0) ++ goto fail; ++ } else if (dpp_test == DPP_TEST_NO_R_AUTH_AUTH_RESP) { ++ wpa_printf(MSG_INFO, "DPP: TESTING - no R-Auth"); ++ w_r_auth = NULL; ++ wrapped_r_auth_len = 0; ++ } else if (dpp_test == DPP_TEST_NO_STATUS_AUTH_RESP) { ++ wpa_printf(MSG_INFO, "DPP: TESTING - no Status"); ++ status = 255; ++ } else if (dpp_test == DPP_TEST_INVALID_STATUS_AUTH_RESP) { ++ wpa_printf(MSG_INFO, "DPP: TESTING - invalid Status"); ++ status = 254; ++ } else if (dpp_test == DPP_TEST_NO_R_NONCE_AUTH_RESP) { ++ wpa_printf(MSG_INFO, "DPP: TESTING - no R-nonce"); ++ r_nonce = NULL; ++ } else if (dpp_test == DPP_TEST_NO_I_NONCE_AUTH_RESP) { ++ wpa_printf(MSG_INFO, "DPP: TESTING - no I-nonce"); ++ i_nonce = NULL; ++ } ++#endif /* CONFIG_TESTING_OPTIONS */ ++ ++ msg = dpp_auth_build_resp(auth, status, pr, nonce_len, ++ r_pubkey_hash, i_pubkey_hash, ++ r_nonce, i_nonce, ++ w_r_auth, wrapped_r_auth_len, ++ auth->k2); ++ if (!msg) ++ goto fail; ++ wpabuf_free(auth->resp_msg); ++ auth->resp_msg = msg; ++ ret = 0; ++fail: ++ wpabuf_free(pr); ++ return ret; ++} ++ ++ ++static int dpp_auth_build_resp_status(struct dpp_authentication *auth, ++ enum dpp_status_error status) ++{ ++ struct wpabuf *msg; ++ const u8 *r_pubkey_hash, *i_pubkey_hash, *i_nonce; ++#ifdef CONFIG_TESTING_OPTIONS ++ u8 test_hash[SHA256_MAC_LEN]; ++#endif /* CONFIG_TESTING_OPTIONS */ ++ ++ if (!auth->own_bi) ++ return -1; ++ wpa_printf(MSG_DEBUG, "DPP: Build Authentication Response"); ++ ++ r_pubkey_hash = auth->own_bi->pubkey_hash; ++ if (auth->peer_bi) ++ i_pubkey_hash = auth->peer_bi->pubkey_hash; ++ else ++ i_pubkey_hash = NULL; ++ ++ i_nonce = auth->i_nonce; ++ ++#ifdef CONFIG_TESTING_OPTIONS ++ if (dpp_test == DPP_TEST_NO_R_BOOTSTRAP_KEY_HASH_AUTH_RESP) { ++ wpa_printf(MSG_INFO, "DPP: TESTING - no R-Bootstrap Key Hash"); ++ r_pubkey_hash = NULL; ++ } else if (dpp_test == ++ DPP_TEST_INVALID_R_BOOTSTRAP_KEY_HASH_AUTH_RESP) { ++ wpa_printf(MSG_INFO, ++ "DPP: TESTING - invalid R-Bootstrap Key Hash"); ++ os_memcpy(test_hash, r_pubkey_hash, SHA256_MAC_LEN); ++ test_hash[SHA256_MAC_LEN - 1] ^= 0x01; ++ r_pubkey_hash = test_hash; ++ } else if (dpp_test == DPP_TEST_NO_I_BOOTSTRAP_KEY_HASH_AUTH_RESP) { ++ wpa_printf(MSG_INFO, "DPP: TESTING - no I-Bootstrap Key Hash"); ++ i_pubkey_hash = NULL; ++ } else if (dpp_test == ++ DPP_TEST_INVALID_I_BOOTSTRAP_KEY_HASH_AUTH_RESP) { ++ wpa_printf(MSG_INFO, ++ "DPP: TESTING - invalid I-Bootstrap Key Hash"); ++ if (i_pubkey_hash) ++ os_memcpy(test_hash, i_pubkey_hash, SHA256_MAC_LEN); ++ else ++ os_memset(test_hash, 0, SHA256_MAC_LEN); ++ test_hash[SHA256_MAC_LEN - 1] ^= 0x01; ++ i_pubkey_hash = test_hash; ++ } else if (dpp_test == DPP_TEST_NO_STATUS_AUTH_RESP) { ++ wpa_printf(MSG_INFO, "DPP: TESTING - no Status"); ++ status = 255; ++ } else if (dpp_test == DPP_TEST_NO_I_NONCE_AUTH_RESP) { ++ wpa_printf(MSG_INFO, "DPP: TESTING - no I-nonce"); ++ i_nonce = NULL; ++ } ++#endif /* CONFIG_TESTING_OPTIONS */ ++ ++ msg = dpp_auth_build_resp(auth, status, NULL, auth->curve->nonce_len, ++ r_pubkey_hash, i_pubkey_hash, ++ NULL, i_nonce, NULL, 0, auth->k1); ++ if (!msg) ++ return -1; ++ wpabuf_free(auth->resp_msg); ++ auth->resp_msg = msg; ++ return 0; ++} ++ ++ ++struct dpp_authentication * ++dpp_auth_req_rx(void *msg_ctx, u8 dpp_allowed_roles, int qr_mutual, ++ struct dpp_bootstrap_info *peer_bi, ++ struct dpp_bootstrap_info *own_bi, ++ unsigned int freq, const u8 *hdr, const u8 *attr_start, ++ size_t attr_len) ++{ ++ EVP_PKEY *pi = NULL; ++ EVP_PKEY_CTX *ctx = NULL; ++ size_t secret_len; ++ const u8 *addr[2]; ++ size_t len[2]; ++ u8 *unwrapped = NULL; ++ size_t unwrapped_len = 0; ++ const u8 *wrapped_data, *i_proto, *i_nonce, *i_capab, *i_bootstrap, ++ *channel; ++ u16 wrapped_data_len, i_proto_len, i_nonce_len, i_capab_len, ++ i_bootstrap_len, channel_len; ++ struct dpp_authentication *auth = NULL; ++#ifdef CONFIG_DPP2 ++ const u8 *version; ++ u16 version_len; ++#endif /* CONFIG_DPP2 */ ++ ++#ifdef CONFIG_TESTING_OPTIONS ++ if (dpp_test == DPP_TEST_STOP_AT_AUTH_REQ) { ++ wpa_printf(MSG_INFO, ++ "DPP: TESTING - stop at Authentication Request"); ++ return NULL; ++ } ++#endif /* CONFIG_TESTING_OPTIONS */ ++ ++ wrapped_data = dpp_get_attr(attr_start, attr_len, DPP_ATTR_WRAPPED_DATA, ++ &wrapped_data_len); ++ if (!wrapped_data || wrapped_data_len < AES_BLOCK_SIZE) { ++ wpa_msg(msg_ctx, MSG_INFO, DPP_EVENT_FAIL ++ "Missing or invalid required Wrapped Data attribute"); ++ return NULL; ++ } ++ wpa_hexdump(MSG_MSGDUMP, "DPP: Wrapped Data", ++ wrapped_data, wrapped_data_len); ++ attr_len = wrapped_data - 4 - attr_start; ++ ++ auth = os_zalloc(sizeof(*auth)); ++ if (!auth) ++ goto fail; ++ auth->msg_ctx = msg_ctx; ++ auth->peer_bi = peer_bi; ++ auth->own_bi = own_bi; ++ auth->curve = own_bi->curve; ++ auth->curr_freq = freq; ++ ++ auth->peer_version = 1; /* default to the first version */ ++#ifdef CONFIG_DPP2 ++ version = dpp_get_attr(attr_start, attr_len, DPP_ATTR_PROTOCOL_VERSION, ++ &version_len); ++ if (version) { ++ if (version_len < 1 || version[0] == 0) { ++ dpp_auth_fail(auth, ++ "Invalid Protocol Version attribute"); ++ goto fail; ++ } ++ auth->peer_version = version[0]; ++ wpa_printf(MSG_DEBUG, "DPP: Peer protocol version %u", ++ auth->peer_version); ++ } ++#endif /* CONFIG_DPP2 */ ++ ++ channel = dpp_get_attr(attr_start, attr_len, DPP_ATTR_CHANNEL, ++ &channel_len); ++ if (channel) { ++ int neg_freq; ++ ++ if (channel_len < 2) { ++ dpp_auth_fail(auth, "Too short Channel attribute"); ++ goto fail; ++ } ++ ++ neg_freq = ieee80211_chan_to_freq(NULL, channel[0], channel[1]); ++ wpa_printf(MSG_DEBUG, ++ "DPP: Initiator requested different channel for negotiation: op_class=%u channel=%u --> freq=%d", ++ channel[0], channel[1], neg_freq); ++ if (neg_freq < 0) { ++ dpp_auth_fail(auth, ++ "Unsupported Channel attribute value"); ++ goto fail; ++ } ++ ++ if (auth->curr_freq != (unsigned int) neg_freq) { ++ wpa_printf(MSG_DEBUG, ++ "DPP: Changing negotiation channel from %u MHz to %u MHz", ++ freq, neg_freq); ++ auth->curr_freq = neg_freq; ++ } ++ } ++ ++ i_proto = dpp_get_attr(attr_start, attr_len, DPP_ATTR_I_PROTOCOL_KEY, ++ &i_proto_len); ++ if (!i_proto) { ++ dpp_auth_fail(auth, ++ "Missing required Initiator Protocol Key attribute"); ++ goto fail; ++ } ++ wpa_hexdump(MSG_MSGDUMP, "DPP: Initiator Protocol Key", ++ i_proto, i_proto_len); ++ ++ /* M = bR * PI */ ++ pi = dpp_set_pubkey_point(own_bi->pubkey, i_proto, i_proto_len); ++ if (!pi) { ++ dpp_auth_fail(auth, "Invalid Initiator Protocol Key"); ++ goto fail; ++ } ++ dpp_debug_print_key("Peer (Initiator) Protocol Key", pi); ++ ++ ctx = EVP_PKEY_CTX_new(own_bi->pubkey, NULL); ++ if (!ctx || ++ EVP_PKEY_derive_init(ctx) != 1 || ++ EVP_PKEY_derive_set_peer(ctx, pi) != 1 || ++ EVP_PKEY_derive(ctx, NULL, &secret_len) != 1 || ++ secret_len > DPP_MAX_SHARED_SECRET_LEN || ++ EVP_PKEY_derive(ctx, auth->Mx, &secret_len) != 1) { ++ wpa_printf(MSG_ERROR, ++ "DPP: Failed to derive ECDH shared secret: %s", ++ ERR_error_string(ERR_get_error(), NULL)); ++ dpp_auth_fail(auth, "Failed to derive ECDH shared secret"); ++ goto fail; ++ } ++ auth->secret_len = secret_len; ++ EVP_PKEY_CTX_free(ctx); ++ ctx = NULL; ++ ++ wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (M.x)", ++ auth->Mx, auth->secret_len); ++ auth->Mx_len = auth->secret_len; ++ ++ if (dpp_derive_k1(auth->Mx, auth->secret_len, auth->k1, ++ auth->curve->hash_len) < 0) ++ goto fail; ++ ++ addr[0] = hdr; ++ len[0] = DPP_HDR_LEN; ++ addr[1] = attr_start; ++ len[1] = attr_len; ++ wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]); ++ wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]); ++ wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext", ++ wrapped_data, wrapped_data_len); ++ unwrapped_len = wrapped_data_len - AES_BLOCK_SIZE; ++ unwrapped = os_malloc(unwrapped_len); ++ if (!unwrapped) ++ goto fail; ++ if (aes_siv_decrypt(auth->k1, auth->curve->hash_len, ++ wrapped_data, wrapped_data_len, ++ 2, addr, len, unwrapped) < 0) { ++ dpp_auth_fail(auth, "AES-SIV decryption failed"); ++ goto fail; ++ } ++ wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext", ++ unwrapped, unwrapped_len); ++ ++ if (dpp_check_attrs(unwrapped, unwrapped_len) < 0) { ++ dpp_auth_fail(auth, "Invalid attribute in unwrapped data"); ++ goto fail; ++ } ++ ++ i_nonce = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_I_NONCE, ++ &i_nonce_len); ++ if (!i_nonce || i_nonce_len != auth->curve->nonce_len) { ++ dpp_auth_fail(auth, "Missing or invalid I-nonce"); ++ goto fail; ++ } ++ wpa_hexdump(MSG_DEBUG, "DPP: I-nonce", i_nonce, i_nonce_len); ++ os_memcpy(auth->i_nonce, i_nonce, i_nonce_len); ++ ++ i_capab = dpp_get_attr(unwrapped, unwrapped_len, ++ DPP_ATTR_I_CAPABILITIES, ++ &i_capab_len); ++ if (!i_capab || i_capab_len < 1) { ++ dpp_auth_fail(auth, "Missing or invalid I-capabilities"); ++ goto fail; ++ } ++ auth->i_capab = i_capab[0]; ++ wpa_printf(MSG_DEBUG, "DPP: I-capabilities: 0x%02x", auth->i_capab); ++ ++ bin_clear_free(unwrapped, unwrapped_len); ++ unwrapped = NULL; ++ ++ switch (auth->i_capab & DPP_CAPAB_ROLE_MASK) { ++ case DPP_CAPAB_ENROLLEE: ++ if (!(dpp_allowed_roles & DPP_CAPAB_CONFIGURATOR)) { ++ wpa_printf(MSG_DEBUG, ++ "DPP: Local policy does not allow Configurator role"); ++ goto not_compatible; ++ } ++ wpa_printf(MSG_DEBUG, "DPP: Acting as Configurator"); ++ auth->configurator = 1; ++ break; ++ case DPP_CAPAB_CONFIGURATOR: ++ if (!(dpp_allowed_roles & DPP_CAPAB_ENROLLEE)) { ++ wpa_printf(MSG_DEBUG, ++ "DPP: Local policy does not allow Enrollee role"); ++ goto not_compatible; ++ } ++ wpa_printf(MSG_DEBUG, "DPP: Acting as Enrollee"); ++ auth->configurator = 0; ++ break; ++ case DPP_CAPAB_CONFIGURATOR | DPP_CAPAB_ENROLLEE: ++ if (dpp_allowed_roles & DPP_CAPAB_ENROLLEE) { ++ wpa_printf(MSG_DEBUG, "DPP: Acting as Enrollee"); ++ auth->configurator = 0; ++ } else if (dpp_allowed_roles & DPP_CAPAB_CONFIGURATOR) { ++ wpa_printf(MSG_DEBUG, "DPP: Acting as Configurator"); ++ auth->configurator = 1; ++ } else { ++ wpa_printf(MSG_DEBUG, ++ "DPP: Local policy does not allow Configurator/Enrollee role"); ++ goto not_compatible; ++ } ++ break; ++ default: ++ wpa_printf(MSG_DEBUG, "DPP: Unexpected role in I-capabilities"); ++ wpa_msg(auth->msg_ctx, MSG_INFO, ++ DPP_EVENT_FAIL "Invalid role in I-capabilities 0x%02x", ++ auth->i_capab & DPP_CAPAB_ROLE_MASK); ++ goto fail; ++ } ++ ++ auth->peer_protocol_key = pi; ++ pi = NULL; ++ if (qr_mutual && !peer_bi && own_bi->type == DPP_BOOTSTRAP_QR_CODE) { ++ char hex[SHA256_MAC_LEN * 2 + 1]; ++ ++ wpa_printf(MSG_DEBUG, ++ "DPP: Mutual authentication required with QR Codes, but peer info is not yet available - request more time"); ++ if (dpp_auth_build_resp_status(auth, ++ DPP_STATUS_RESPONSE_PENDING) < 0) ++ goto fail; ++ i_bootstrap = dpp_get_attr(attr_start, attr_len, ++ DPP_ATTR_I_BOOTSTRAP_KEY_HASH, ++ &i_bootstrap_len); ++ if (i_bootstrap && i_bootstrap_len == SHA256_MAC_LEN) { ++ auth->response_pending = 1; ++ os_memcpy(auth->waiting_pubkey_hash, ++ i_bootstrap, i_bootstrap_len); ++ wpa_snprintf_hex(hex, sizeof(hex), i_bootstrap, ++ i_bootstrap_len); ++ } else { ++ hex[0] = '\0'; ++ } ++ ++ wpa_msg(auth->msg_ctx, MSG_INFO, DPP_EVENT_SCAN_PEER_QR_CODE ++ "%s", hex); ++ return auth; ++ } ++ if (dpp_auth_build_resp_ok(auth) < 0) ++ goto fail; ++ ++ return auth; ++ ++not_compatible: ++ wpa_msg(auth->msg_ctx, MSG_INFO, DPP_EVENT_NOT_COMPATIBLE ++ "i-capab=0x%02x", auth->i_capab); ++ if (dpp_allowed_roles & DPP_CAPAB_CONFIGURATOR) ++ auth->configurator = 1; ++ else ++ auth->configurator = 0; ++ auth->peer_protocol_key = pi; ++ pi = NULL; ++ if (dpp_auth_build_resp_status(auth, DPP_STATUS_NOT_COMPATIBLE) < 0) ++ goto fail; ++ ++ auth->remove_on_tx_status = 1; ++ return auth; ++fail: ++ bin_clear_free(unwrapped, unwrapped_len); ++ EVP_PKEY_free(pi); ++ EVP_PKEY_CTX_free(ctx); ++ dpp_auth_deinit(auth); ++ return NULL; ++} ++ ++ ++int dpp_notify_new_qr_code(struct dpp_authentication *auth, ++ struct dpp_bootstrap_info *peer_bi) ++{ ++ if (!auth || !auth->response_pending || ++ os_memcmp(auth->waiting_pubkey_hash, peer_bi->pubkey_hash, ++ SHA256_MAC_LEN) != 0) ++ return 0; ++ ++ wpa_printf(MSG_DEBUG, ++ "DPP: New scanned QR Code has matching public key that was needed to continue DPP Authentication exchange with " ++ MACSTR, MAC2STR(auth->peer_mac_addr)); ++ auth->peer_bi = peer_bi; ++ ++ if (dpp_auth_build_resp_ok(auth) < 0) ++ return -1; ++ ++ return 1; ++} ++ ++ ++static struct wpabuf * dpp_auth_build_conf(struct dpp_authentication *auth, ++ enum dpp_status_error status) ++{ ++ struct wpabuf *msg; ++ u8 i_auth[4 + DPP_MAX_HASH_LEN]; ++ size_t i_auth_len; ++ u8 r_nonce[4 + DPP_MAX_NONCE_LEN]; ++ size_t r_nonce_len; ++ const u8 *addr[2]; ++ size_t len[2], attr_len; ++ u8 *wrapped_i_auth; ++ u8 *wrapped_r_nonce; ++ u8 *attr_start, *attr_end; ++ const u8 *r_pubkey_hash, *i_pubkey_hash; ++#ifdef CONFIG_TESTING_OPTIONS ++ u8 test_hash[SHA256_MAC_LEN]; ++#endif /* CONFIG_TESTING_OPTIONS */ ++ ++ wpa_printf(MSG_DEBUG, "DPP: Build Authentication Confirmation"); ++ ++ i_auth_len = 4 + auth->curve->hash_len; ++ r_nonce_len = 4 + auth->curve->nonce_len; ++ /* Build DPP Authentication Confirmation frame attributes */ ++ attr_len = 4 + 1 + 2 * (4 + SHA256_MAC_LEN) + ++ 4 + i_auth_len + r_nonce_len + AES_BLOCK_SIZE; ++#ifdef CONFIG_TESTING_OPTIONS ++ if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_AUTH_CONF) ++ attr_len += 5; ++#endif /* CONFIG_TESTING_OPTIONS */ ++ msg = dpp_alloc_msg(DPP_PA_AUTHENTICATION_CONF, attr_len); ++ if (!msg) ++ goto fail; ++ ++ attr_start = wpabuf_put(msg, 0); ++ ++ r_pubkey_hash = auth->peer_bi->pubkey_hash; ++ if (auth->own_bi) ++ i_pubkey_hash = auth->own_bi->pubkey_hash; ++ else ++ i_pubkey_hash = NULL; ++ ++#ifdef CONFIG_TESTING_OPTIONS ++ if (dpp_test == DPP_TEST_NO_STATUS_AUTH_CONF) { ++ wpa_printf(MSG_INFO, "DPP: TESTING - no Status"); ++ goto skip_status; ++ } else if (dpp_test == DPP_TEST_INVALID_STATUS_AUTH_CONF) { ++ wpa_printf(MSG_INFO, "DPP: TESTING - invalid Status"); ++ status = 254; ++ } ++#endif /* CONFIG_TESTING_OPTIONS */ ++ ++ /* DPP Status */ ++ dpp_build_attr_status(msg, status); ++ ++#ifdef CONFIG_TESTING_OPTIONS ++skip_status: ++ if (dpp_test == DPP_TEST_NO_R_BOOTSTRAP_KEY_HASH_AUTH_CONF) { ++ wpa_printf(MSG_INFO, "DPP: TESTING - no R-Bootstrap Key Hash"); ++ r_pubkey_hash = NULL; ++ } else if (dpp_test == ++ DPP_TEST_INVALID_R_BOOTSTRAP_KEY_HASH_AUTH_CONF) { ++ wpa_printf(MSG_INFO, ++ "DPP: TESTING - invalid R-Bootstrap Key Hash"); ++ os_memcpy(test_hash, r_pubkey_hash, SHA256_MAC_LEN); ++ test_hash[SHA256_MAC_LEN - 1] ^= 0x01; ++ r_pubkey_hash = test_hash; ++ } else if (dpp_test == DPP_TEST_NO_I_BOOTSTRAP_KEY_HASH_AUTH_CONF) { ++ wpa_printf(MSG_INFO, "DPP: TESTING - no I-Bootstrap Key Hash"); ++ i_pubkey_hash = NULL; ++ } else if (dpp_test == ++ DPP_TEST_INVALID_I_BOOTSTRAP_KEY_HASH_AUTH_CONF) { ++ wpa_printf(MSG_INFO, ++ "DPP: TESTING - invalid I-Bootstrap Key Hash"); ++ if (i_pubkey_hash) ++ os_memcpy(test_hash, i_pubkey_hash, SHA256_MAC_LEN); ++ else ++ os_memset(test_hash, 0, SHA256_MAC_LEN); ++ test_hash[SHA256_MAC_LEN - 1] ^= 0x01; ++ i_pubkey_hash = test_hash; ++ } ++#endif /* CONFIG_TESTING_OPTIONS */ ++ ++ /* Responder Bootstrapping Key Hash */ ++ dpp_build_attr_r_bootstrap_key_hash(msg, r_pubkey_hash); ++ ++ /* Initiator Bootstrapping Key Hash (mutual authentication) */ ++ dpp_build_attr_i_bootstrap_key_hash(msg, i_pubkey_hash); ++ ++#ifdef CONFIG_TESTING_OPTIONS ++ if (dpp_test == DPP_TEST_NO_WRAPPED_DATA_AUTH_CONF) ++ goto skip_wrapped_data; ++ if (dpp_test == DPP_TEST_NO_I_AUTH_AUTH_CONF) ++ i_auth_len = 0; ++#endif /* CONFIG_TESTING_OPTIONS */ ++ ++ attr_end = wpabuf_put(msg, 0); ++ ++ /* OUI, OUI type, Crypto Suite, DPP frame type */ ++ addr[0] = wpabuf_head_u8(msg) + 2; ++ len[0] = 3 + 1 + 1 + 1; ++ wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]); ++ ++ /* Attributes before Wrapped Data */ ++ addr[1] = attr_start; ++ len[1] = attr_end - attr_start; ++ wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]); ++ ++ if (status == DPP_STATUS_OK) { ++ /* I-auth wrapped with ke */ ++ wpabuf_put_le16(msg, DPP_ATTR_WRAPPED_DATA); ++ wpabuf_put_le16(msg, i_auth_len + AES_BLOCK_SIZE); ++ wrapped_i_auth = wpabuf_put(msg, i_auth_len + AES_BLOCK_SIZE); ++ ++#ifdef CONFIG_TESTING_OPTIONS ++ if (dpp_test == DPP_TEST_NO_I_AUTH_AUTH_CONF) ++ goto skip_i_auth; ++#endif /* CONFIG_TESTING_OPTIONS */ ++ ++ /* I-auth = H(R-nonce | I-nonce | PR.x | PI.x | BR.x | [BI.x |] ++ * 1) */ ++ WPA_PUT_LE16(i_auth, DPP_ATTR_I_AUTH_TAG); ++ WPA_PUT_LE16(&i_auth[2], auth->curve->hash_len); ++ if (dpp_gen_i_auth(auth, i_auth + 4) < 0) ++ goto fail; ++ ++#ifdef CONFIG_TESTING_OPTIONS ++ if (dpp_test == DPP_TEST_I_AUTH_MISMATCH_AUTH_CONF) { ++ wpa_printf(MSG_INFO, "DPP: TESTING - I-auth mismatch"); ++ i_auth[4 + auth->curve->hash_len / 2] ^= 0x01; ++ } ++skip_i_auth: ++#endif /* CONFIG_TESTING_OPTIONS */ ++ if (aes_siv_encrypt(auth->ke, auth->curve->hash_len, ++ i_auth, i_auth_len, ++ 2, addr, len, wrapped_i_auth) < 0) ++ goto fail; ++ wpa_hexdump(MSG_DEBUG, "DPP: {I-auth}ke", ++ wrapped_i_auth, i_auth_len + AES_BLOCK_SIZE); ++ } else { ++ /* R-nonce wrapped with k2 */ ++ wpabuf_put_le16(msg, DPP_ATTR_WRAPPED_DATA); ++ wpabuf_put_le16(msg, r_nonce_len + AES_BLOCK_SIZE); ++ wrapped_r_nonce = wpabuf_put(msg, r_nonce_len + AES_BLOCK_SIZE); ++ ++ WPA_PUT_LE16(r_nonce, DPP_ATTR_R_NONCE); ++ WPA_PUT_LE16(&r_nonce[2], auth->curve->nonce_len); ++ os_memcpy(r_nonce + 4, auth->r_nonce, auth->curve->nonce_len); ++ ++ if (aes_siv_encrypt(auth->k2, auth->curve->hash_len, ++ r_nonce, r_nonce_len, ++ 2, addr, len, wrapped_r_nonce) < 0) ++ goto fail; ++ wpa_hexdump(MSG_DEBUG, "DPP: {R-nonce}k2", ++ wrapped_r_nonce, r_nonce_len + AES_BLOCK_SIZE); ++ } ++ ++#ifdef CONFIG_TESTING_OPTIONS ++ if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_AUTH_CONF) { ++ wpa_printf(MSG_INFO, "DPP: TESTING - attr after Wrapped Data"); ++ dpp_build_attr_status(msg, DPP_STATUS_OK); ++ } ++skip_wrapped_data: ++#endif /* CONFIG_TESTING_OPTIONS */ ++ ++ wpa_hexdump_buf(MSG_DEBUG, ++ "DPP: Authentication Confirmation frame attributes", ++ msg); ++ if (status == DPP_STATUS_OK) ++ dpp_auth_success(auth); ++ ++ return msg; ++ ++fail: ++ wpabuf_free(msg); ++ return NULL; ++} ++ ++ ++static void ++dpp_auth_resp_rx_status(struct dpp_authentication *auth, const u8 *hdr, ++ const u8 *attr_start, size_t attr_len, ++ const u8 *wrapped_data, u16 wrapped_data_len, ++ enum dpp_status_error status) ++{ ++ const u8 *addr[2]; ++ size_t len[2]; ++ u8 *unwrapped = NULL; ++ size_t unwrapped_len = 0; ++ const u8 *i_nonce, *r_capab; ++ u16 i_nonce_len, r_capab_len; ++ ++ if (status == DPP_STATUS_NOT_COMPATIBLE) { ++ wpa_printf(MSG_DEBUG, ++ "DPP: Responder reported incompatible roles"); ++ } else if (status == DPP_STATUS_RESPONSE_PENDING) { ++ wpa_printf(MSG_DEBUG, ++ "DPP: Responder reported more time needed"); ++ } else { ++ wpa_printf(MSG_DEBUG, ++ "DPP: Responder reported failure (status %d)", ++ status); ++ dpp_auth_fail(auth, "Responder reported failure"); ++ return; ++ } ++ ++ addr[0] = hdr; ++ len[0] = DPP_HDR_LEN; ++ addr[1] = attr_start; ++ len[1] = attr_len; ++ wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]); ++ wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]); ++ wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext", ++ wrapped_data, wrapped_data_len); ++ unwrapped_len = wrapped_data_len - AES_BLOCK_SIZE; ++ unwrapped = os_malloc(unwrapped_len); ++ if (!unwrapped) ++ goto fail; ++ if (aes_siv_decrypt(auth->k1, auth->curve->hash_len, ++ wrapped_data, wrapped_data_len, ++ 2, addr, len, unwrapped) < 0) { ++ dpp_auth_fail(auth, "AES-SIV decryption failed"); ++ goto fail; ++ } ++ wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext", ++ unwrapped, unwrapped_len); ++ ++ if (dpp_check_attrs(unwrapped, unwrapped_len) < 0) { ++ dpp_auth_fail(auth, "Invalid attribute in unwrapped data"); ++ goto fail; ++ } ++ ++ i_nonce = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_I_NONCE, ++ &i_nonce_len); ++ if (!i_nonce || i_nonce_len != auth->curve->nonce_len) { ++ dpp_auth_fail(auth, "Missing or invalid I-nonce"); ++ goto fail; ++ } ++ wpa_hexdump(MSG_DEBUG, "DPP: I-nonce", i_nonce, i_nonce_len); ++ if (os_memcmp(auth->i_nonce, i_nonce, i_nonce_len) != 0) { ++ dpp_auth_fail(auth, "I-nonce mismatch"); ++ goto fail; ++ } ++ ++ r_capab = dpp_get_attr(unwrapped, unwrapped_len, ++ DPP_ATTR_R_CAPABILITIES, ++ &r_capab_len); ++ if (!r_capab || r_capab_len < 1) { ++ dpp_auth_fail(auth, "Missing or invalid R-capabilities"); ++ goto fail; ++ } ++ auth->r_capab = r_capab[0]; ++ wpa_printf(MSG_DEBUG, "DPP: R-capabilities: 0x%02x", auth->r_capab); ++ if (status == DPP_STATUS_NOT_COMPATIBLE) { ++ wpa_msg(auth->msg_ctx, MSG_INFO, DPP_EVENT_NOT_COMPATIBLE ++ "r-capab=0x%02x", auth->r_capab); ++ } else if (status == DPP_STATUS_RESPONSE_PENDING) { ++ u8 role = auth->r_capab & DPP_CAPAB_ROLE_MASK; ++ ++ if ((auth->configurator && role != DPP_CAPAB_ENROLLEE) || ++ (!auth->configurator && role != DPP_CAPAB_CONFIGURATOR)) { ++ wpa_msg(auth->msg_ctx, MSG_INFO, ++ DPP_EVENT_FAIL "Unexpected role in R-capabilities 0x%02x", ++ role); ++ } else { ++ wpa_printf(MSG_DEBUG, ++ "DPP: Continue waiting for full DPP Authentication Response"); ++ wpa_msg(auth->msg_ctx, MSG_INFO, ++ DPP_EVENT_RESPONSE_PENDING "%s", ++ auth->tmp_own_bi ? auth->tmp_own_bi->uri : ""); ++ } ++ } ++fail: ++ bin_clear_free(unwrapped, unwrapped_len); ++} ++ ++ ++struct wpabuf * ++dpp_auth_resp_rx(struct dpp_authentication *auth, const u8 *hdr, ++ const u8 *attr_start, size_t attr_len) ++{ ++ EVP_PKEY *pr; ++ EVP_PKEY_CTX *ctx = NULL; ++ size_t secret_len; ++ const u8 *addr[2]; ++ size_t len[2]; ++ u8 *unwrapped = NULL, *unwrapped2 = NULL; ++ size_t unwrapped_len = 0, unwrapped2_len = 0; ++ const u8 *r_bootstrap, *i_bootstrap, *wrapped_data, *status, *r_proto, ++ *r_nonce, *i_nonce, *r_capab, *wrapped2, *r_auth; ++ u16 r_bootstrap_len, i_bootstrap_len, wrapped_data_len, status_len, ++ r_proto_len, r_nonce_len, i_nonce_len, r_capab_len, ++ wrapped2_len, r_auth_len; ++ u8 r_auth2[DPP_MAX_HASH_LEN]; ++ u8 role; ++#ifdef CONFIG_DPP2 ++ const u8 *version; ++ u16 version_len; ++#endif /* CONFIG_DPP2 */ ++ ++#ifdef CONFIG_TESTING_OPTIONS ++ if (dpp_test == DPP_TEST_STOP_AT_AUTH_RESP) { ++ wpa_printf(MSG_INFO, ++ "DPP: TESTING - stop at Authentication Response"); ++ return NULL; ++ } ++#endif /* CONFIG_TESTING_OPTIONS */ ++ ++ if (!auth->initiator || !auth->peer_bi) { ++ dpp_auth_fail(auth, "Unexpected Authentication Response"); ++ return NULL; ++ } ++ ++ auth->waiting_auth_resp = 0; ++ ++ wrapped_data = dpp_get_attr(attr_start, attr_len, DPP_ATTR_WRAPPED_DATA, ++ &wrapped_data_len); ++ if (!wrapped_data || wrapped_data_len < AES_BLOCK_SIZE) { ++ dpp_auth_fail(auth, ++ "Missing or invalid required Wrapped Data attribute"); ++ return NULL; ++ } ++ wpa_hexdump(MSG_DEBUG, "DPP: Wrapped data", ++ wrapped_data, wrapped_data_len); ++ ++ attr_len = wrapped_data - 4 - attr_start; ++ ++ r_bootstrap = dpp_get_attr(attr_start, attr_len, ++ DPP_ATTR_R_BOOTSTRAP_KEY_HASH, ++ &r_bootstrap_len); ++ if (!r_bootstrap || r_bootstrap_len != SHA256_MAC_LEN) { ++ dpp_auth_fail(auth, ++ "Missing or invalid required Responder Bootstrapping Key Hash attribute"); ++ return NULL; ++ } ++ wpa_hexdump(MSG_DEBUG, "DPP: Responder Bootstrapping Key Hash", ++ r_bootstrap, r_bootstrap_len); ++ if (os_memcmp(r_bootstrap, auth->peer_bi->pubkey_hash, ++ SHA256_MAC_LEN) != 0) { ++ dpp_auth_fail(auth, ++ "Unexpected Responder Bootstrapping Key Hash value"); ++ wpa_hexdump(MSG_DEBUG, ++ "DPP: Expected Responder Bootstrapping Key Hash", ++ auth->peer_bi->pubkey_hash, SHA256_MAC_LEN); ++ return NULL; ++ } ++ ++ i_bootstrap = dpp_get_attr(attr_start, attr_len, ++ DPP_ATTR_I_BOOTSTRAP_KEY_HASH, ++ &i_bootstrap_len); ++ if (i_bootstrap) { ++ if (i_bootstrap_len != SHA256_MAC_LEN) { ++ dpp_auth_fail(auth, ++ "Invalid Initiator Bootstrapping Key Hash attribute"); ++ return NULL; ++ } ++ wpa_hexdump(MSG_MSGDUMP, ++ "DPP: Initiator Bootstrapping Key Hash", ++ i_bootstrap, i_bootstrap_len); ++ if (!auth->own_bi || ++ os_memcmp(i_bootstrap, auth->own_bi->pubkey_hash, ++ SHA256_MAC_LEN) != 0) { ++ dpp_auth_fail(auth, ++ "Initiator Bootstrapping Key Hash attribute did not match"); ++ return NULL; ++ } ++ } else if (auth->own_bi && auth->own_bi->type == DPP_BOOTSTRAP_PKEX) { ++ /* PKEX bootstrapping mandates use of mutual authentication */ ++ dpp_auth_fail(auth, ++ "Missing Initiator Bootstrapping Key Hash attribute"); ++ return NULL; ++ } ++ ++ auth->peer_version = 1; /* default to the first version */ ++#ifdef CONFIG_DPP2 ++ version = dpp_get_attr(attr_start, attr_len, DPP_ATTR_PROTOCOL_VERSION, ++ &version_len); ++ if (version) { ++ if (version_len < 1 || version[0] == 0) { ++ dpp_auth_fail(auth, ++ "Invalid Protocol Version attribute"); ++ return NULL; ++ } ++ auth->peer_version = version[0]; ++ wpa_printf(MSG_DEBUG, "DPP: Peer protocol version %u", ++ auth->peer_version); ++ } ++#endif /* CONFIG_DPP2 */ ++ ++ status = dpp_get_attr(attr_start, attr_len, DPP_ATTR_STATUS, ++ &status_len); ++ if (!status || status_len < 1) { ++ dpp_auth_fail(auth, ++ "Missing or invalid required DPP Status attribute"); ++ return NULL; ++ } ++ wpa_printf(MSG_DEBUG, "DPP: Status %u", status[0]); ++ auth->auth_resp_status = status[0]; ++ if (status[0] != DPP_STATUS_OK) { ++ dpp_auth_resp_rx_status(auth, hdr, attr_start, ++ attr_len, wrapped_data, ++ wrapped_data_len, status[0]); ++ return NULL; ++ } ++ ++ if (!i_bootstrap && auth->own_bi) { ++ wpa_printf(MSG_DEBUG, ++ "DPP: Responder decided not to use mutual authentication"); ++ auth->own_bi = NULL; ++ } ++ ++ wpa_msg(auth->msg_ctx, MSG_INFO, DPP_EVENT_AUTH_DIRECTION "mutual=%d", ++ auth->own_bi != NULL); ++ ++ r_proto = dpp_get_attr(attr_start, attr_len, DPP_ATTR_R_PROTOCOL_KEY, ++ &r_proto_len); ++ if (!r_proto) { ++ dpp_auth_fail(auth, ++ "Missing required Responder Protocol Key attribute"); ++ return NULL; ++ } ++ wpa_hexdump(MSG_MSGDUMP, "DPP: Responder Protocol Key", ++ r_proto, r_proto_len); ++ ++ /* N = pI * PR */ ++ pr = dpp_set_pubkey_point(auth->own_protocol_key, r_proto, r_proto_len); ++ if (!pr) { ++ dpp_auth_fail(auth, "Invalid Responder Protocol Key"); ++ return NULL; ++ } ++ dpp_debug_print_key("Peer (Responder) Protocol Key", pr); ++ ++ ctx = EVP_PKEY_CTX_new(auth->own_protocol_key, NULL); ++ if (!ctx || ++ EVP_PKEY_derive_init(ctx) != 1 || ++ EVP_PKEY_derive_set_peer(ctx, pr) != 1 || ++ EVP_PKEY_derive(ctx, NULL, &secret_len) != 1 || ++ secret_len > DPP_MAX_SHARED_SECRET_LEN || ++ EVP_PKEY_derive(ctx, auth->Nx, &secret_len) != 1) { ++ wpa_printf(MSG_ERROR, ++ "DPP: Failed to derive ECDH shared secret: %s", ++ ERR_error_string(ERR_get_error(), NULL)); ++ dpp_auth_fail(auth, "Failed to derive ECDH shared secret"); ++ goto fail; ++ } ++ EVP_PKEY_CTX_free(ctx); ++ ctx = NULL; ++ auth->peer_protocol_key = pr; ++ pr = NULL; ++ ++ wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (N.x)", ++ auth->Nx, auth->secret_len); ++ auth->Nx_len = auth->secret_len; ++ ++ if (dpp_derive_k2(auth->Nx, auth->secret_len, auth->k2, ++ auth->curve->hash_len) < 0) ++ goto fail; ++ ++ addr[0] = hdr; ++ len[0] = DPP_HDR_LEN; ++ addr[1] = attr_start; ++ len[1] = attr_len; ++ wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]); ++ wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]); ++ wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext", ++ wrapped_data, wrapped_data_len); ++ unwrapped_len = wrapped_data_len - AES_BLOCK_SIZE; ++ unwrapped = os_malloc(unwrapped_len); ++ if (!unwrapped) ++ goto fail; ++ if (aes_siv_decrypt(auth->k2, auth->curve->hash_len, ++ wrapped_data, wrapped_data_len, ++ 2, addr, len, unwrapped) < 0) { ++ dpp_auth_fail(auth, "AES-SIV decryption failed"); ++ goto fail; ++ } ++ wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext", ++ unwrapped, unwrapped_len); ++ ++ if (dpp_check_attrs(unwrapped, unwrapped_len) < 0) { ++ dpp_auth_fail(auth, "Invalid attribute in unwrapped data"); ++ goto fail; ++ } ++ ++ r_nonce = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_R_NONCE, ++ &r_nonce_len); ++ if (!r_nonce || r_nonce_len != auth->curve->nonce_len) { ++ dpp_auth_fail(auth, "DPP: Missing or invalid R-nonce"); ++ goto fail; ++ } ++ wpa_hexdump(MSG_DEBUG, "DPP: R-nonce", r_nonce, r_nonce_len); ++ os_memcpy(auth->r_nonce, r_nonce, r_nonce_len); ++ ++ i_nonce = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_I_NONCE, ++ &i_nonce_len); ++ if (!i_nonce || i_nonce_len != auth->curve->nonce_len) { ++ dpp_auth_fail(auth, "Missing or invalid I-nonce"); ++ goto fail; ++ } ++ wpa_hexdump(MSG_DEBUG, "DPP: I-nonce", i_nonce, i_nonce_len); ++ if (os_memcmp(auth->i_nonce, i_nonce, i_nonce_len) != 0) { ++ dpp_auth_fail(auth, "I-nonce mismatch"); ++ goto fail; ++ } ++ ++ if (auth->own_bi) { ++ /* Mutual authentication */ ++ if (dpp_auth_derive_l_initiator(auth) < 0) ++ goto fail; ++ } ++ ++ r_capab = dpp_get_attr(unwrapped, unwrapped_len, ++ DPP_ATTR_R_CAPABILITIES, ++ &r_capab_len); ++ if (!r_capab || r_capab_len < 1) { ++ dpp_auth_fail(auth, "Missing or invalid R-capabilities"); ++ goto fail; ++ } ++ auth->r_capab = r_capab[0]; ++ wpa_printf(MSG_DEBUG, "DPP: R-capabilities: 0x%02x", auth->r_capab); ++ role = auth->r_capab & DPP_CAPAB_ROLE_MASK; ++ if ((auth->allowed_roles == ++ (DPP_CAPAB_CONFIGURATOR | DPP_CAPAB_ENROLLEE)) && ++ (role == DPP_CAPAB_CONFIGURATOR || role == DPP_CAPAB_ENROLLEE)) { ++ /* Peer selected its role, so move from "either role" to the ++ * role that is compatible with peer's selection. */ ++ auth->configurator = role == DPP_CAPAB_ENROLLEE; ++ wpa_printf(MSG_DEBUG, "DPP: Acting as %s", ++ auth->configurator ? "Configurator" : "Enrollee"); ++ } else if ((auth->configurator && role != DPP_CAPAB_ENROLLEE) || ++ (!auth->configurator && role != DPP_CAPAB_CONFIGURATOR)) { ++ wpa_printf(MSG_DEBUG, "DPP: Incompatible role selection"); ++ wpa_msg(auth->msg_ctx, MSG_INFO, DPP_EVENT_FAIL ++ "Unexpected role in R-capabilities 0x%02x", ++ role); ++ if (role != DPP_CAPAB_ENROLLEE && ++ role != DPP_CAPAB_CONFIGURATOR) ++ goto fail; ++ bin_clear_free(unwrapped, unwrapped_len); ++ auth->remove_on_tx_status = 1; ++ return dpp_auth_build_conf(auth, DPP_STATUS_NOT_COMPATIBLE); ++ } ++ ++ wrapped2 = dpp_get_attr(unwrapped, unwrapped_len, ++ DPP_ATTR_WRAPPED_DATA, &wrapped2_len); ++ if (!wrapped2 || wrapped2_len < AES_BLOCK_SIZE) { ++ dpp_auth_fail(auth, ++ "Missing or invalid Secondary Wrapped Data"); ++ goto fail; ++ } ++ ++ wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext", ++ wrapped2, wrapped2_len); ++ ++ if (dpp_derive_ke(auth, auth->ke, auth->curve->hash_len) < 0) ++ goto fail; ++ ++ unwrapped2_len = wrapped2_len - AES_BLOCK_SIZE; ++ unwrapped2 = os_malloc(unwrapped2_len); ++ if (!unwrapped2) ++ goto fail; ++ if (aes_siv_decrypt(auth->ke, auth->curve->hash_len, ++ wrapped2, wrapped2_len, ++ 0, NULL, NULL, unwrapped2) < 0) { ++ dpp_auth_fail(auth, "AES-SIV decryption failed"); ++ goto fail; ++ } ++ wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext", ++ unwrapped2, unwrapped2_len); ++ ++ if (dpp_check_attrs(unwrapped2, unwrapped2_len) < 0) { ++ dpp_auth_fail(auth, ++ "Invalid attribute in secondary unwrapped data"); ++ goto fail; ++ } ++ ++ r_auth = dpp_get_attr(unwrapped2, unwrapped2_len, DPP_ATTR_R_AUTH_TAG, ++ &r_auth_len); ++ if (!r_auth || r_auth_len != auth->curve->hash_len) { ++ dpp_auth_fail(auth, ++ "Missing or invalid Responder Authenticating Tag"); ++ goto fail; ++ } ++ wpa_hexdump(MSG_DEBUG, "DPP: Received Responder Authenticating Tag", ++ r_auth, r_auth_len); ++ /* R-auth' = H(I-nonce | R-nonce | PI.x | PR.x | [BI.x |] BR.x | 0) */ ++ if (dpp_gen_r_auth(auth, r_auth2) < 0) ++ goto fail; ++ wpa_hexdump(MSG_DEBUG, "DPP: Calculated Responder Authenticating Tag", ++ r_auth2, r_auth_len); ++ if (os_memcmp(r_auth, r_auth2, r_auth_len) != 0) { ++ dpp_auth_fail(auth, "Mismatching Responder Authenticating Tag"); ++ bin_clear_free(unwrapped, unwrapped_len); ++ bin_clear_free(unwrapped2, unwrapped2_len); ++ auth->remove_on_tx_status = 1; ++ return dpp_auth_build_conf(auth, DPP_STATUS_AUTH_FAILURE); ++ } ++ ++ bin_clear_free(unwrapped, unwrapped_len); ++ bin_clear_free(unwrapped2, unwrapped2_len); ++ ++#ifdef CONFIG_TESTING_OPTIONS ++ if (dpp_test == DPP_TEST_AUTH_RESP_IN_PLACE_OF_CONF) { ++ wpa_printf(MSG_INFO, ++ "DPP: TESTING - Authentication Response in place of Confirm"); ++ if (dpp_auth_build_resp_ok(auth) < 0) ++ return NULL; ++ return wpabuf_dup(auth->resp_msg); ++ } ++#endif /* CONFIG_TESTING_OPTIONS */ ++ ++ return dpp_auth_build_conf(auth, DPP_STATUS_OK); ++ ++fail: ++ bin_clear_free(unwrapped, unwrapped_len); ++ bin_clear_free(unwrapped2, unwrapped2_len); ++ EVP_PKEY_free(pr); ++ EVP_PKEY_CTX_free(ctx); ++ return NULL; ++} ++ ++ ++static int dpp_auth_conf_rx_failure(struct dpp_authentication *auth, ++ const u8 *hdr, ++ const u8 *attr_start, size_t attr_len, ++ const u8 *wrapped_data, ++ u16 wrapped_data_len, ++ enum dpp_status_error status) ++{ ++ const u8 *addr[2]; ++ size_t len[2]; ++ u8 *unwrapped = NULL; ++ size_t unwrapped_len = 0; ++ const u8 *r_nonce; ++ u16 r_nonce_len; ++ ++ /* Authentication Confirm failure cases are expected to include ++ * {R-nonce}k2 in the Wrapped Data attribute. */ ++ ++ addr[0] = hdr; ++ len[0] = DPP_HDR_LEN; ++ addr[1] = attr_start; ++ len[1] = attr_len; ++ wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]); ++ wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]); ++ wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext", ++ wrapped_data, wrapped_data_len); ++ unwrapped_len = wrapped_data_len - AES_BLOCK_SIZE; ++ unwrapped = os_malloc(unwrapped_len); ++ if (!unwrapped) { ++ dpp_auth_fail(auth, "Authentication failed"); ++ goto fail; ++ } ++ if (aes_siv_decrypt(auth->k2, auth->curve->hash_len, ++ wrapped_data, wrapped_data_len, ++ 2, addr, len, unwrapped) < 0) { ++ dpp_auth_fail(auth, "AES-SIV decryption failed"); ++ goto fail; ++ } ++ wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext", ++ unwrapped, unwrapped_len); ++ ++ if (dpp_check_attrs(unwrapped, unwrapped_len) < 0) { ++ dpp_auth_fail(auth, "Invalid attribute in unwrapped data"); ++ goto fail; ++ } ++ ++ r_nonce = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_R_NONCE, ++ &r_nonce_len); ++ if (!r_nonce || r_nonce_len != auth->curve->nonce_len) { ++ dpp_auth_fail(auth, "DPP: Missing or invalid R-nonce"); ++ goto fail; ++ } ++ if (os_memcmp(r_nonce, auth->r_nonce, r_nonce_len) != 0) { ++ wpa_hexdump(MSG_DEBUG, "DPP: Received R-nonce", ++ r_nonce, r_nonce_len); ++ wpa_hexdump(MSG_DEBUG, "DPP: Expected R-nonce", ++ auth->r_nonce, r_nonce_len); ++ dpp_auth_fail(auth, "R-nonce mismatch"); ++ goto fail; ++ } ++ ++ if (status == DPP_STATUS_NOT_COMPATIBLE) ++ dpp_auth_fail(auth, "Peer reported incompatible R-capab role"); ++ else if (status == DPP_STATUS_AUTH_FAILURE) ++ dpp_auth_fail(auth, "Peer reported authentication failure)"); ++ ++fail: ++ bin_clear_free(unwrapped, unwrapped_len); ++ return -1; ++} ++ ++ ++int dpp_auth_conf_rx(struct dpp_authentication *auth, const u8 *hdr, ++ const u8 *attr_start, size_t attr_len) ++{ ++ const u8 *r_bootstrap, *i_bootstrap, *wrapped_data, *status, *i_auth; ++ u16 r_bootstrap_len, i_bootstrap_len, wrapped_data_len, status_len, ++ i_auth_len; ++ const u8 *addr[2]; ++ size_t len[2]; ++ u8 *unwrapped = NULL; ++ size_t unwrapped_len = 0; ++ u8 i_auth2[DPP_MAX_HASH_LEN]; ++ ++#ifdef CONFIG_TESTING_OPTIONS ++ if (dpp_test == DPP_TEST_STOP_AT_AUTH_CONF) { ++ wpa_printf(MSG_INFO, ++ "DPP: TESTING - stop at Authentication Confirm"); ++ return -1; ++ } ++#endif /* CONFIG_TESTING_OPTIONS */ ++ ++ if (auth->initiator || !auth->own_bi) { ++ dpp_auth_fail(auth, "Unexpected Authentication Confirm"); ++ return -1; ++ } ++ ++ auth->waiting_auth_conf = 0; ++ ++ wrapped_data = dpp_get_attr(attr_start, attr_len, DPP_ATTR_WRAPPED_DATA, ++ &wrapped_data_len); ++ if (!wrapped_data || wrapped_data_len < AES_BLOCK_SIZE) { ++ dpp_auth_fail(auth, ++ "Missing or invalid required Wrapped Data attribute"); ++ return -1; ++ } ++ wpa_hexdump(MSG_DEBUG, "DPP: Wrapped data", ++ wrapped_data, wrapped_data_len); ++ ++ attr_len = wrapped_data - 4 - attr_start; ++ ++ r_bootstrap = dpp_get_attr(attr_start, attr_len, ++ DPP_ATTR_R_BOOTSTRAP_KEY_HASH, ++ &r_bootstrap_len); ++ if (!r_bootstrap || r_bootstrap_len != SHA256_MAC_LEN) { ++ dpp_auth_fail(auth, ++ "Missing or invalid required Responder Bootstrapping Key Hash attribute"); ++ return -1; ++ } ++ wpa_hexdump(MSG_DEBUG, "DPP: Responder Bootstrapping Key Hash", ++ r_bootstrap, r_bootstrap_len); ++ if (os_memcmp(r_bootstrap, auth->own_bi->pubkey_hash, ++ SHA256_MAC_LEN) != 0) { ++ wpa_hexdump(MSG_DEBUG, ++ "DPP: Expected Responder Bootstrapping Key Hash", ++ auth->peer_bi->pubkey_hash, SHA256_MAC_LEN); ++ dpp_auth_fail(auth, ++ "Responder Bootstrapping Key Hash mismatch"); ++ return -1; ++ } ++ ++ i_bootstrap = dpp_get_attr(attr_start, attr_len, ++ DPP_ATTR_I_BOOTSTRAP_KEY_HASH, ++ &i_bootstrap_len); ++ if (i_bootstrap) { ++ if (i_bootstrap_len != SHA256_MAC_LEN) { ++ dpp_auth_fail(auth, ++ "Invalid Initiator Bootstrapping Key Hash attribute"); ++ return -1; ++ } ++ wpa_hexdump(MSG_MSGDUMP, ++ "DPP: Initiator Bootstrapping Key Hash", ++ i_bootstrap, i_bootstrap_len); ++ if (!auth->peer_bi || ++ os_memcmp(i_bootstrap, auth->peer_bi->pubkey_hash, ++ SHA256_MAC_LEN) != 0) { ++ dpp_auth_fail(auth, ++ "Initiator Bootstrapping Key Hash mismatch"); ++ return -1; ++ } ++ } else if (auth->peer_bi) { ++ /* Mutual authentication and peer did not include its ++ * Bootstrapping Key Hash attribute. */ ++ dpp_auth_fail(auth, ++ "Missing Initiator Bootstrapping Key Hash attribute"); ++ return -1; ++ } ++ ++ status = dpp_get_attr(attr_start, attr_len, DPP_ATTR_STATUS, ++ &status_len); ++ if (!status || status_len < 1) { ++ dpp_auth_fail(auth, ++ "Missing or invalid required DPP Status attribute"); ++ return -1; ++ } ++ wpa_printf(MSG_DEBUG, "DPP: Status %u", status[0]); ++ if (status[0] == DPP_STATUS_NOT_COMPATIBLE || ++ status[0] == DPP_STATUS_AUTH_FAILURE) ++ return dpp_auth_conf_rx_failure(auth, hdr, attr_start, ++ attr_len, wrapped_data, ++ wrapped_data_len, status[0]); ++ ++ if (status[0] != DPP_STATUS_OK) { ++ dpp_auth_fail(auth, "Authentication failed"); ++ return -1; ++ } ++ ++ addr[0] = hdr; ++ len[0] = DPP_HDR_LEN; ++ addr[1] = attr_start; ++ len[1] = attr_len; ++ wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]); ++ wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]); ++ wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext", ++ wrapped_data, wrapped_data_len); ++ unwrapped_len = wrapped_data_len - AES_BLOCK_SIZE; ++ unwrapped = os_malloc(unwrapped_len); ++ if (!unwrapped) ++ return -1; ++ if (aes_siv_decrypt(auth->ke, auth->curve->hash_len, ++ wrapped_data, wrapped_data_len, ++ 2, addr, len, unwrapped) < 0) { ++ dpp_auth_fail(auth, "AES-SIV decryption failed"); ++ goto fail; ++ } ++ wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext", ++ unwrapped, unwrapped_len); ++ ++ if (dpp_check_attrs(unwrapped, unwrapped_len) < 0) { ++ dpp_auth_fail(auth, "Invalid attribute in unwrapped data"); ++ goto fail; ++ } ++ ++ i_auth = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_I_AUTH_TAG, ++ &i_auth_len); ++ if (!i_auth || i_auth_len != auth->curve->hash_len) { ++ dpp_auth_fail(auth, ++ "Missing or invalid Initiator Authenticating Tag"); ++ goto fail; ++ } ++ wpa_hexdump(MSG_DEBUG, "DPP: Received Initiator Authenticating Tag", ++ i_auth, i_auth_len); ++ /* I-auth' = H(R-nonce | I-nonce | PR.x | PI.x | BR.x | [BI.x |] 1) */ ++ if (dpp_gen_i_auth(auth, i_auth2) < 0) ++ goto fail; ++ wpa_hexdump(MSG_DEBUG, "DPP: Calculated Initiator Authenticating Tag", ++ i_auth2, i_auth_len); ++ if (os_memcmp(i_auth, i_auth2, i_auth_len) != 0) { ++ dpp_auth_fail(auth, "Mismatching Initiator Authenticating Tag"); ++ goto fail; ++ } ++ ++ bin_clear_free(unwrapped, unwrapped_len); ++ dpp_auth_success(auth); ++ return 0; ++fail: ++ bin_clear_free(unwrapped, unwrapped_len); ++ return -1; ++} ++ ++ ++static int bin_str_eq(const char *val, size_t len, const char *cmp) ++{ ++ return os_strlen(cmp) == len && os_memcmp(val, cmp, len) == 0; ++} ++ ++ ++struct dpp_configuration * dpp_configuration_alloc(const char *type) ++{ ++ struct dpp_configuration *conf; ++ const char *end; ++ size_t len; ++ ++ conf = os_zalloc(sizeof(*conf)); ++ if (!conf) ++ goto fail; ++ ++ end = os_strchr(type, ' '); ++ if (end) ++ len = end - type; ++ else ++ len = os_strlen(type); ++ ++ if (bin_str_eq(type, len, "psk")) ++ conf->akm = DPP_AKM_PSK; ++ else if (bin_str_eq(type, len, "sae")) ++ conf->akm = DPP_AKM_SAE; ++ else if (bin_str_eq(type, len, "psk-sae") || ++ bin_str_eq(type, len, "psk+sae")) ++ conf->akm = DPP_AKM_PSK_SAE; ++ else if (bin_str_eq(type, len, "sae-dpp") || ++ bin_str_eq(type, len, "dpp+sae")) ++ conf->akm = DPP_AKM_SAE_DPP; ++ else if (bin_str_eq(type, len, "psk-sae-dpp") || ++ bin_str_eq(type, len, "dpp+psk+sae")) ++ conf->akm = DPP_AKM_PSK_SAE_DPP; ++ else if (bin_str_eq(type, len, "dpp")) ++ conf->akm = DPP_AKM_DPP; ++ else ++ goto fail; ++ ++ return conf; ++fail: ++ dpp_configuration_free(conf); ++ return NULL; ++} ++ ++ ++int dpp_akm_psk(enum dpp_akm akm) ++{ ++ return akm == DPP_AKM_PSK || akm == DPP_AKM_PSK_SAE || ++ akm == DPP_AKM_PSK_SAE_DPP; ++} ++ ++ ++int dpp_akm_sae(enum dpp_akm akm) ++{ ++ return akm == DPP_AKM_SAE || akm == DPP_AKM_PSK_SAE || ++ akm == DPP_AKM_SAE_DPP || akm == DPP_AKM_PSK_SAE_DPP; ++} ++ ++ ++int dpp_akm_legacy(enum dpp_akm akm) ++{ ++ return akm == DPP_AKM_PSK || akm == DPP_AKM_PSK_SAE || ++ akm == DPP_AKM_SAE; ++} ++ ++ ++int dpp_akm_dpp(enum dpp_akm akm) ++{ ++ return akm == DPP_AKM_DPP || akm == DPP_AKM_SAE_DPP || ++ akm == DPP_AKM_PSK_SAE_DPP; ++} ++ ++ ++int dpp_akm_ver2(enum dpp_akm akm) ++{ ++ return akm == DPP_AKM_SAE_DPP || akm == DPP_AKM_PSK_SAE_DPP; ++} ++ ++ ++int dpp_configuration_valid(const struct dpp_configuration *conf) ++{ ++ if (conf->ssid_len == 0) ++ return 0; ++ if (dpp_akm_psk(conf->akm) && !conf->passphrase && !conf->psk_set) ++ return 0; ++ if (dpp_akm_sae(conf->akm) && !conf->passphrase) ++ return 0; ++ return 1; ++} ++ ++ ++void dpp_configuration_free(struct dpp_configuration *conf) ++{ ++ if (!conf) ++ return; ++ str_clear_free(conf->passphrase); ++ os_free(conf->group_id); ++ bin_clear_free(conf, sizeof(*conf)); ++} ++ ++ ++static int dpp_configuration_parse(struct dpp_authentication *auth, ++ const char *cmd) ++{ ++ const char *pos, *end; ++ struct dpp_configuration *conf_sta = NULL, *conf_ap = NULL; ++ struct dpp_configuration *conf = NULL; ++ ++ pos = os_strstr(cmd, " conf=sta-"); ++ if (pos) { ++ conf_sta = dpp_configuration_alloc(pos + 10); ++ if (!conf_sta) ++ goto fail; ++ conf = conf_sta; ++ } ++ ++ pos = os_strstr(cmd, " conf=ap-"); ++ if (pos) { ++ conf_ap = dpp_configuration_alloc(pos + 9); ++ if (!conf_ap) ++ goto fail; ++ conf = conf_ap; ++ } ++ ++ if (!conf) ++ return 0; ++ ++ pos = os_strstr(cmd, " ssid="); ++ if (pos) { ++ pos += 6; ++ end = os_strchr(pos, ' '); ++ conf->ssid_len = end ? (size_t) (end - pos) : os_strlen(pos); ++ conf->ssid_len /= 2; ++ if (conf->ssid_len > sizeof(conf->ssid) || ++ hexstr2bin(pos, conf->ssid, conf->ssid_len) < 0) ++ goto fail; ++ } else { ++#ifdef CONFIG_TESTING_OPTIONS ++ /* use a default SSID for legacy testing reasons */ ++ os_memcpy(conf->ssid, "test", 4); ++ conf->ssid_len = 4; ++#else /* CONFIG_TESTING_OPTIONS */ ++ goto fail; ++#endif /* CONFIG_TESTING_OPTIONS */ ++ } ++ ++ pos = os_strstr(cmd, " pass="); ++ if (pos) { ++ size_t pass_len; ++ ++ pos += 6; ++ end = os_strchr(pos, ' '); ++ pass_len = end ? (size_t) (end - pos) : os_strlen(pos); ++ pass_len /= 2; ++ if (pass_len > 63 || pass_len < 8) ++ goto fail; ++ conf->passphrase = os_zalloc(pass_len + 1); ++ if (!conf->passphrase || ++ hexstr2bin(pos, (u8 *) conf->passphrase, pass_len) < 0) ++ goto fail; ++ } ++ ++ pos = os_strstr(cmd, " psk="); ++ if (pos) { ++ pos += 5; ++ if (hexstr2bin(pos, conf->psk, PMK_LEN) < 0) ++ goto fail; ++ conf->psk_set = 1; ++ } ++ ++ pos = os_strstr(cmd, " group_id="); ++ if (pos) { ++ size_t group_id_len; ++ ++ pos += 10; ++ end = os_strchr(pos, ' '); ++ group_id_len = end ? (size_t) (end - pos) : os_strlen(pos); ++ conf->group_id = os_malloc(group_id_len + 1); ++ if (!conf->group_id) ++ goto fail; ++ os_memcpy(conf->group_id, pos, group_id_len); ++ conf->group_id[group_id_len] = '\0'; ++ } ++ ++ pos = os_strstr(cmd, " expiry="); ++ if (pos) { ++ long int val; ++ ++ pos += 8; ++ val = strtol(pos, NULL, 0); ++ if (val <= 0) ++ goto fail; ++ conf->netaccesskey_expiry = val; ++ } ++ ++ if (!dpp_configuration_valid(conf)) ++ goto fail; ++ ++ auth->conf_sta = conf_sta; ++ auth->conf_ap = conf_ap; ++ return 0; ++ ++fail: ++ dpp_configuration_free(conf_sta); ++ dpp_configuration_free(conf_ap); ++ return -1; ++} ++ ++ ++static struct dpp_configurator * ++dpp_configurator_get_id(struct dpp_global *dpp, unsigned int id) ++{ ++ struct dpp_configurator *conf; ++ ++ if (!dpp) ++ return NULL; ++ ++ dl_list_for_each(conf, &dpp->configurator, ++ struct dpp_configurator, list) { ++ if (conf->id == id) ++ return conf; ++ } ++ return NULL; ++} ++ ++ ++int dpp_set_configurator(struct dpp_global *dpp, void *msg_ctx, ++ struct dpp_authentication *auth, ++ const char *cmd) ++{ ++ const char *pos; ++ ++ if (!cmd) ++ return 0; ++ ++ wpa_printf(MSG_DEBUG, "DPP: Set configurator parameters: %s", cmd); ++ ++ pos = os_strstr(cmd, " configurator="); ++ if (pos) { ++ pos += 14; ++ auth->conf = dpp_configurator_get_id(dpp, atoi(pos)); ++ if (!auth->conf) { ++ wpa_printf(MSG_INFO, ++ "DPP: Could not find the specified configurator"); ++ return -1; ++ } ++ } ++ ++ if (dpp_configuration_parse(auth, cmd) < 0) { ++ wpa_msg(msg_ctx, MSG_INFO, ++ "DPP: Failed to set configurator parameters"); ++ return -1; ++ } ++ return 0; ++} ++ ++ ++void dpp_auth_deinit(struct dpp_authentication *auth) ++{ ++ if (!auth) ++ return; ++ dpp_configuration_free(auth->conf_ap); ++ dpp_configuration_free(auth->conf_sta); ++ EVP_PKEY_free(auth->own_protocol_key); ++ EVP_PKEY_free(auth->peer_protocol_key); ++ wpabuf_free(auth->req_msg); ++ wpabuf_free(auth->resp_msg); ++ wpabuf_free(auth->conf_req); ++ os_free(auth->connector); ++ wpabuf_free(auth->net_access_key); ++ wpabuf_free(auth->c_sign_key); ++ dpp_bootstrap_info_free(auth->tmp_own_bi); ++#ifdef CONFIG_TESTING_OPTIONS ++ os_free(auth->config_obj_override); ++ os_free(auth->discovery_override); ++ os_free(auth->groups_override); ++#endif /* CONFIG_TESTING_OPTIONS */ ++ bin_clear_free(auth, sizeof(*auth)); ++} ++ ++ ++static struct wpabuf * ++dpp_build_conf_start(struct dpp_authentication *auth, ++ struct dpp_configuration *conf, size_t tailroom) ++{ ++ struct wpabuf *buf; ++ char ssid[6 * sizeof(conf->ssid) + 1]; ++ ++#ifdef CONFIG_TESTING_OPTIONS ++ if (auth->discovery_override) ++ tailroom += os_strlen(auth->discovery_override); ++#endif /* CONFIG_TESTING_OPTIONS */ ++ ++ buf = wpabuf_alloc(200 + tailroom); ++ if (!buf) ++ return NULL; ++ wpabuf_put_str(buf, "{\"wi-fi_tech\":\"infra\",\"discovery\":"); ++#ifdef CONFIG_TESTING_OPTIONS ++ if (auth->discovery_override) { ++ wpa_printf(MSG_DEBUG, "DPP: TESTING - discovery override: '%s'", ++ auth->discovery_override); ++ wpabuf_put_str(buf, auth->discovery_override); ++ wpabuf_put_u8(buf, ','); ++ return buf; ++ } ++#endif /* CONFIG_TESTING_OPTIONS */ ++ wpabuf_put_str(buf, "{\"ssid\":\""); ++ json_escape_string(ssid, sizeof(ssid), ++ (const char *) conf->ssid, conf->ssid_len); ++ wpabuf_put_str(buf, ssid); ++ wpabuf_put_str(buf, "\"},"); ++ ++ return buf; ++} ++ ++ ++static int dpp_build_jwk(struct wpabuf *buf, const char *name, EVP_PKEY *key, ++ const char *kid, const struct dpp_curve_params *curve) ++{ ++ struct wpabuf *pub; ++ const u8 *pos; ++ char *x = NULL, *y = NULL; ++ int ret = -1; ++ ++ pub = dpp_get_pubkey_point(key, 0); ++ if (!pub) ++ goto fail; ++ pos = wpabuf_head(pub); ++ x = (char *) base64_url_encode(pos, curve->prime_len, NULL, 0); ++ pos += curve->prime_len; ++ y = (char *) base64_url_encode(pos, curve->prime_len, NULL, 0); ++ if (!x || !y) ++ goto fail; ++ ++ wpabuf_put_str(buf, "\""); ++ wpabuf_put_str(buf, name); ++ wpabuf_put_str(buf, "\":{\"kty\":\"EC\",\"crv\":\""); ++ wpabuf_put_str(buf, curve->jwk_crv); ++ wpabuf_put_str(buf, "\",\"x\":\""); ++ wpabuf_put_str(buf, x); ++ wpabuf_put_str(buf, "\",\"y\":\""); ++ wpabuf_put_str(buf, y); ++ if (kid) { ++ wpabuf_put_str(buf, "\",\"kid\":\""); ++ wpabuf_put_str(buf, kid); ++ } ++ wpabuf_put_str(buf, "\"}"); ++ ret = 0; ++fail: ++ wpabuf_free(pub); ++ os_free(x); ++ os_free(y); ++ return ret; ++} ++ ++ ++static void dpp_build_legacy_cred_params(struct wpabuf *buf, ++ struct dpp_configuration *conf) ++{ ++ if (conf->passphrase && os_strlen(conf->passphrase) < 64) { ++ char pass[63 * 6 + 1]; ++ ++ json_escape_string(pass, sizeof(pass), conf->passphrase, ++ os_strlen(conf->passphrase)); ++ wpabuf_put_str(buf, "\"pass\":\""); ++ wpabuf_put_str(buf, pass); ++ wpabuf_put_str(buf, "\""); ++ os_memset(pass, 0, sizeof(pass)); ++ } else if (conf->psk_set) { ++ char psk[2 * sizeof(conf->psk) + 1]; ++ ++ wpa_snprintf_hex(psk, sizeof(psk), ++ conf->psk, sizeof(conf->psk)); ++ wpabuf_put_str(buf, "\"psk_hex\":\""); ++ wpabuf_put_str(buf, psk); ++ wpabuf_put_str(buf, "\""); ++ os_memset(psk, 0, sizeof(psk)); ++ } ++} ++ ++ ++static struct wpabuf * ++dpp_build_conf_obj_dpp(struct dpp_authentication *auth, int ap, ++ struct dpp_configuration *conf) ++{ ++ struct wpabuf *buf = NULL; ++ char *signed1 = NULL, *signed2 = NULL, *signed3 = NULL; ++ size_t tailroom; ++ const struct dpp_curve_params *curve; ++ char jws_prot_hdr[100]; ++ size_t signed1_len, signed2_len, signed3_len; ++ struct wpabuf *dppcon = NULL; ++ unsigned char *signature = NULL; ++ const unsigned char *p; ++ size_t signature_len; ++ EVP_MD_CTX *md_ctx = NULL; ++ ECDSA_SIG *sig = NULL; ++ char *dot = "."; ++ const EVP_MD *sign_md; ++ const BIGNUM *r, *s; ++ size_t extra_len = 1000; ++ int incl_legacy; ++ enum dpp_akm akm; ++ ++ if (!auth->conf) { ++ wpa_printf(MSG_INFO, ++ "DPP: No configurator specified - cannot generate DPP config object"); ++ goto fail; ++ } ++ curve = auth->conf->curve; ++ if (curve->hash_len == SHA256_MAC_LEN) { ++ sign_md = EVP_sha256(); ++ } else if (curve->hash_len == SHA384_MAC_LEN) { ++ sign_md = EVP_sha384(); ++ } else if (curve->hash_len == SHA512_MAC_LEN) { ++ sign_md = EVP_sha512(); ++ } else { ++ wpa_printf(MSG_DEBUG, "DPP: Unknown signature algorithm"); ++ goto fail; ++ } ++ ++ akm = conf->akm; ++ if (dpp_akm_ver2(akm) && auth->peer_version < 2) { ++ wpa_printf(MSG_DEBUG, ++ "DPP: Convert DPP+legacy credential to DPP-only for peer that does not support version 2"); ++ akm = DPP_AKM_DPP; ++ } ++ ++#ifdef CONFIG_TESTING_OPTIONS ++ if (auth->groups_override) ++ extra_len += os_strlen(auth->groups_override); ++#endif /* CONFIG_TESTING_OPTIONS */ ++ ++ if (conf->group_id) ++ extra_len += os_strlen(conf->group_id); ++ ++ /* Connector (JSON dppCon object) */ ++ dppcon = wpabuf_alloc(extra_len + 2 * auth->curve->prime_len * 4 / 3); ++ if (!dppcon) ++ goto fail; ++#ifdef CONFIG_TESTING_OPTIONS ++ if (auth->groups_override) { ++ wpabuf_put_u8(dppcon, '{'); ++ if (auth->groups_override) { ++ wpa_printf(MSG_DEBUG, ++ "DPP: TESTING - groups override: '%s'", ++ auth->groups_override); ++ wpabuf_put_str(dppcon, "\"groups\":"); ++ wpabuf_put_str(dppcon, auth->groups_override); ++ wpabuf_put_u8(dppcon, ','); ++ } ++ goto skip_groups; ++ } ++#endif /* CONFIG_TESTING_OPTIONS */ ++ wpabuf_printf(dppcon, "{\"groups\":[{\"groupId\":\"%s\",", ++ conf->group_id ? conf->group_id : "*"); ++ wpabuf_printf(dppcon, "\"netRole\":\"%s\"}],", ap ? "ap" : "sta"); ++#ifdef CONFIG_TESTING_OPTIONS ++skip_groups: ++#endif /* CONFIG_TESTING_OPTIONS */ ++ if (dpp_build_jwk(dppcon, "netAccessKey", auth->peer_protocol_key, NULL, ++ auth->curve) < 0) { ++ wpa_printf(MSG_DEBUG, "DPP: Failed to build netAccessKey JWK"); ++ goto fail; ++ } ++ if (conf->netaccesskey_expiry) { ++ struct os_tm tm; ++ ++ if (os_gmtime(conf->netaccesskey_expiry, &tm) < 0) { ++ wpa_printf(MSG_DEBUG, ++ "DPP: Failed to generate expiry string"); ++ goto fail; ++ } ++ wpabuf_printf(dppcon, ++ ",\"expiry\":\"%04u-%02u-%02uT%02u:%02u:%02uZ\"", ++ tm.year, tm.month, tm.day, ++ tm.hour, tm.min, tm.sec); ++ } ++ wpabuf_put_u8(dppcon, '}'); ++ wpa_printf(MSG_DEBUG, "DPP: dppCon: %s", ++ (const char *) wpabuf_head(dppcon)); ++ ++ os_snprintf(jws_prot_hdr, sizeof(jws_prot_hdr), ++ "{\"typ\":\"dppCon\",\"kid\":\"%s\",\"alg\":\"%s\"}", ++ auth->conf->kid, curve->jws_alg); ++ signed1 = (char *) base64_url_encode((unsigned char *) jws_prot_hdr, ++ os_strlen(jws_prot_hdr), ++ &signed1_len, 0); ++ signed2 = (char *) base64_url_encode(wpabuf_head(dppcon), ++ wpabuf_len(dppcon), ++ &signed2_len, 0); ++ if (!signed1 || !signed2) ++ goto fail; ++ ++ md_ctx = EVP_MD_CTX_create(); ++ if (!md_ctx) ++ goto fail; ++ ++ ERR_clear_error(); ++ if (EVP_DigestSignInit(md_ctx, NULL, sign_md, NULL, ++ auth->conf->csign) != 1) { ++ wpa_printf(MSG_DEBUG, "DPP: EVP_DigestSignInit failed: %s", ++ ERR_error_string(ERR_get_error(), NULL)); ++ goto fail; ++ } ++ if (EVP_DigestSignUpdate(md_ctx, signed1, signed1_len) != 1 || ++ EVP_DigestSignUpdate(md_ctx, dot, 1) != 1 || ++ EVP_DigestSignUpdate(md_ctx, signed2, signed2_len) != 1) { ++ wpa_printf(MSG_DEBUG, "DPP: EVP_DigestSignUpdate failed: %s", ++ ERR_error_string(ERR_get_error(), NULL)); ++ goto fail; ++ } ++ if (EVP_DigestSignFinal(md_ctx, NULL, &signature_len) != 1) { ++ wpa_printf(MSG_DEBUG, "DPP: EVP_DigestSignFinal failed: %s", ++ ERR_error_string(ERR_get_error(), NULL)); ++ goto fail; ++ } ++ signature = os_malloc(signature_len); ++ if (!signature) ++ goto fail; ++ if (EVP_DigestSignFinal(md_ctx, signature, &signature_len) != 1) { ++ wpa_printf(MSG_DEBUG, "DPP: EVP_DigestSignFinal failed: %s", ++ ERR_error_string(ERR_get_error(), NULL)); ++ goto fail; ++ } ++ wpa_hexdump(MSG_DEBUG, "DPP: signedConnector ECDSA signature (DER)", ++ signature, signature_len); ++ /* Convert to raw coordinates r,s */ ++ p = signature; ++ sig = d2i_ECDSA_SIG(NULL, &p, signature_len); ++ if (!sig) ++ goto fail; ++ ECDSA_SIG_get0(sig, &r, &s); ++ if (dpp_bn2bin_pad(r, signature, curve->prime_len) < 0 || ++ dpp_bn2bin_pad(s, signature + curve->prime_len, ++ curve->prime_len) < 0) ++ goto fail; ++ signature_len = 2 * curve->prime_len; ++ wpa_hexdump(MSG_DEBUG, "DPP: signedConnector ECDSA signature (raw r,s)", ++ signature, signature_len); ++ signed3 = (char *) base64_url_encode(signature, signature_len, ++ &signed3_len, 0); ++ if (!signed3) ++ goto fail; ++ ++ incl_legacy = dpp_akm_psk(akm) || dpp_akm_sae(akm); ++ tailroom = 1000; ++ tailroom += 2 * curve->prime_len * 4 / 3 + os_strlen(auth->conf->kid); ++ tailroom += signed1_len + signed2_len + signed3_len; ++ if (incl_legacy) ++ tailroom += 1000; ++ buf = dpp_build_conf_start(auth, conf, tailroom); ++ if (!buf) ++ goto fail; ++ ++ wpabuf_printf(buf, "\"cred\":{\"akm\":\"%s\",", dpp_akm_str(akm)); ++ if (incl_legacy) { ++ dpp_build_legacy_cred_params(buf, conf); ++ wpabuf_put_str(buf, ","); ++ } ++ wpabuf_put_str(buf, "\"signedConnector\":\""); ++ wpabuf_put_str(buf, signed1); ++ wpabuf_put_u8(buf, '.'); ++ wpabuf_put_str(buf, signed2); ++ wpabuf_put_u8(buf, '.'); ++ wpabuf_put_str(buf, signed3); ++ wpabuf_put_str(buf, "\","); ++ if (dpp_build_jwk(buf, "csign", auth->conf->csign, auth->conf->kid, ++ curve) < 0) { ++ wpa_printf(MSG_DEBUG, "DPP: Failed to build csign JWK"); ++ goto fail; ++ } ++ ++ wpabuf_put_str(buf, "}}"); ++ ++ wpa_hexdump_ascii_key(MSG_DEBUG, "DPP: Configuration Object", ++ wpabuf_head(buf), wpabuf_len(buf)); ++ ++out: ++ EVP_MD_CTX_destroy(md_ctx); ++ ECDSA_SIG_free(sig); ++ os_free(signed1); ++ os_free(signed2); ++ os_free(signed3); ++ os_free(signature); ++ wpabuf_free(dppcon); ++ return buf; ++fail: ++ wpa_printf(MSG_DEBUG, "DPP: Failed to build configuration object"); ++ wpabuf_free(buf); ++ buf = NULL; ++ goto out; ++} ++ ++ ++static struct wpabuf * ++dpp_build_conf_obj_legacy(struct dpp_authentication *auth, int ap, ++ struct dpp_configuration *conf) ++{ ++ struct wpabuf *buf; ++ ++ buf = dpp_build_conf_start(auth, conf, 1000); ++ if (!buf) ++ return NULL; ++ ++ wpabuf_printf(buf, "\"cred\":{\"akm\":\"%s\",", dpp_akm_str(conf->akm)); ++ dpp_build_legacy_cred_params(buf, conf); ++ wpabuf_put_str(buf, "}}"); ++ ++ wpa_hexdump_ascii_key(MSG_DEBUG, "DPP: Configuration Object (legacy)", ++ wpabuf_head(buf), wpabuf_len(buf)); ++ ++ return buf; ++} ++ ++ ++static struct wpabuf * ++dpp_build_conf_obj(struct dpp_authentication *auth, int ap) ++{ ++ struct dpp_configuration *conf; ++ ++#ifdef CONFIG_TESTING_OPTIONS ++ if (auth->config_obj_override) { ++ wpa_printf(MSG_DEBUG, "DPP: Testing - Config Object override"); ++ return wpabuf_alloc_copy(auth->config_obj_override, ++ os_strlen(auth->config_obj_override)); ++ } ++#endif /* CONFIG_TESTING_OPTIONS */ ++ ++ conf = ap ? auth->conf_ap : auth->conf_sta; ++ if (!conf) { ++ wpa_printf(MSG_DEBUG, ++ "DPP: No configuration available for Enrollee(%s) - reject configuration request", ++ ap ? "ap" : "sta"); ++ return NULL; ++ } ++ ++ if (dpp_akm_dpp(conf->akm)) ++ return dpp_build_conf_obj_dpp(auth, ap, conf); ++ return dpp_build_conf_obj_legacy(auth, ap, conf); ++} ++ ++ ++static struct wpabuf * ++dpp_build_conf_resp(struct dpp_authentication *auth, const u8 *e_nonce, ++ u16 e_nonce_len, int ap) ++{ ++ struct wpabuf *conf; ++ size_t clear_len, attr_len; ++ struct wpabuf *clear = NULL, *msg = NULL; ++ u8 *wrapped; ++ const u8 *addr[1]; ++ size_t len[1]; ++ enum dpp_status_error status; ++ ++ conf = dpp_build_conf_obj(auth, ap); ++ if (conf) { ++ wpa_hexdump_ascii(MSG_DEBUG, "DPP: configurationObject JSON", ++ wpabuf_head(conf), wpabuf_len(conf)); ++ } ++ status = conf ? DPP_STATUS_OK : DPP_STATUS_CONFIGURE_FAILURE; ++ auth->conf_resp_status = status; ++ ++ /* { E-nonce, configurationObject}ke */ ++ clear_len = 4 + e_nonce_len; ++ if (conf) ++ clear_len += 4 + wpabuf_len(conf); ++ clear = wpabuf_alloc(clear_len); ++ attr_len = 4 + 1 + 4 + clear_len + AES_BLOCK_SIZE; ++#ifdef CONFIG_TESTING_OPTIONS ++ if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_CONF_RESP) ++ attr_len += 5; ++#endif /* CONFIG_TESTING_OPTIONS */ ++ msg = wpabuf_alloc(attr_len); ++ if (!clear || !msg) ++ goto fail; ++ ++#ifdef CONFIG_TESTING_OPTIONS ++ if (dpp_test == DPP_TEST_NO_E_NONCE_CONF_RESP) { ++ wpa_printf(MSG_INFO, "DPP: TESTING - no E-nonce"); ++ goto skip_e_nonce; ++ } ++ if (dpp_test == DPP_TEST_E_NONCE_MISMATCH_CONF_RESP) { ++ wpa_printf(MSG_INFO, "DPP: TESTING - E-nonce mismatch"); ++ wpabuf_put_le16(clear, DPP_ATTR_ENROLLEE_NONCE); ++ wpabuf_put_le16(clear, e_nonce_len); ++ wpabuf_put_data(clear, e_nonce, e_nonce_len - 1); ++ wpabuf_put_u8(clear, e_nonce[e_nonce_len - 1] ^ 0x01); ++ goto skip_e_nonce; ++ } ++ if (dpp_test == DPP_TEST_NO_WRAPPED_DATA_CONF_RESP) { ++ wpa_printf(MSG_INFO, "DPP: TESTING - no Wrapped Data"); ++ goto skip_wrapped_data; ++ } ++#endif /* CONFIG_TESTING_OPTIONS */ ++ ++ /* E-nonce */ ++ wpabuf_put_le16(clear, DPP_ATTR_ENROLLEE_NONCE); ++ wpabuf_put_le16(clear, e_nonce_len); ++ wpabuf_put_data(clear, e_nonce, e_nonce_len); ++ ++#ifdef CONFIG_TESTING_OPTIONS ++skip_e_nonce: ++ if (dpp_test == DPP_TEST_NO_CONFIG_OBJ_CONF_RESP) { ++ wpa_printf(MSG_INFO, "DPP: TESTING - Config Object"); ++ goto skip_config_obj; ++ } ++#endif /* CONFIG_TESTING_OPTIONS */ ++ ++ if (conf) { ++ wpabuf_put_le16(clear, DPP_ATTR_CONFIG_OBJ); ++ wpabuf_put_le16(clear, wpabuf_len(conf)); ++ wpabuf_put_buf(clear, conf); ++ } ++ ++#ifdef CONFIG_TESTING_OPTIONS ++skip_config_obj: ++ if (dpp_test == DPP_TEST_NO_STATUS_CONF_RESP) { ++ wpa_printf(MSG_INFO, "DPP: TESTING - Status"); ++ goto skip_status; ++ } ++ if (dpp_test == DPP_TEST_INVALID_STATUS_CONF_RESP) { ++ wpa_printf(MSG_INFO, "DPP: TESTING - invalid Status"); ++ status = 255; ++ } ++#endif /* CONFIG_TESTING_OPTIONS */ ++ ++ /* DPP Status */ ++ dpp_build_attr_status(msg, status); ++ ++#ifdef CONFIG_TESTING_OPTIONS ++skip_status: ++#endif /* CONFIG_TESTING_OPTIONS */ ++ ++ addr[0] = wpabuf_head(msg); ++ len[0] = wpabuf_len(msg); ++ wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD", addr[0], len[0]); ++ ++ wpabuf_put_le16(msg, DPP_ATTR_WRAPPED_DATA); ++ wpabuf_put_le16(msg, wpabuf_len(clear) + AES_BLOCK_SIZE); ++ wrapped = wpabuf_put(msg, wpabuf_len(clear) + AES_BLOCK_SIZE); ++ ++ wpa_hexdump_buf(MSG_DEBUG, "DPP: AES-SIV cleartext", clear); ++ if (aes_siv_encrypt(auth->ke, auth->curve->hash_len, ++ wpabuf_head(clear), wpabuf_len(clear), ++ 1, addr, len, wrapped) < 0) ++ goto fail; ++ wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext", ++ wrapped, wpabuf_len(clear) + AES_BLOCK_SIZE); ++ ++#ifdef CONFIG_TESTING_OPTIONS ++ if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_CONF_RESP) { ++ wpa_printf(MSG_INFO, "DPP: TESTING - attr after Wrapped Data"); ++ dpp_build_attr_status(msg, DPP_STATUS_OK); ++ } ++skip_wrapped_data: ++#endif /* CONFIG_TESTING_OPTIONS */ ++ ++ wpa_hexdump_buf(MSG_DEBUG, ++ "DPP: Configuration Response attributes", msg); ++out: ++ wpabuf_free(conf); ++ wpabuf_free(clear); ++ ++ return msg; ++fail: ++ wpabuf_free(msg); ++ msg = NULL; ++ goto out; ++} ++ ++ ++struct wpabuf * ++dpp_conf_req_rx(struct dpp_authentication *auth, const u8 *attr_start, ++ size_t attr_len) ++{ ++ const u8 *wrapped_data, *e_nonce, *config_attr; ++ u16 wrapped_data_len, e_nonce_len, config_attr_len; ++ u8 *unwrapped = NULL; ++ size_t unwrapped_len = 0; ++ struct wpabuf *resp = NULL; ++ struct json_token *root = NULL, *token; ++ int ap; ++ ++#ifdef CONFIG_TESTING_OPTIONS ++ if (dpp_test == DPP_TEST_STOP_AT_CONF_REQ) { ++ wpa_printf(MSG_INFO, ++ "DPP: TESTING - stop at Config Request"); ++ return NULL; ++ } ++#endif /* CONFIG_TESTING_OPTIONS */ ++ ++ if (dpp_check_attrs(attr_start, attr_len) < 0) { ++ dpp_auth_fail(auth, "Invalid attribute in config request"); ++ return NULL; ++ } ++ ++ wrapped_data = dpp_get_attr(attr_start, attr_len, DPP_ATTR_WRAPPED_DATA, ++ &wrapped_data_len); ++ if (!wrapped_data || wrapped_data_len < AES_BLOCK_SIZE) { ++ dpp_auth_fail(auth, ++ "Missing or invalid required Wrapped Data attribute"); ++ return NULL; ++ } ++ ++ wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext", ++ wrapped_data, wrapped_data_len); ++ unwrapped_len = wrapped_data_len - AES_BLOCK_SIZE; ++ unwrapped = os_malloc(unwrapped_len); ++ if (!unwrapped) ++ return NULL; ++ if (aes_siv_decrypt(auth->ke, auth->curve->hash_len, ++ wrapped_data, wrapped_data_len, ++ 0, NULL, NULL, unwrapped) < 0) { ++ dpp_auth_fail(auth, "AES-SIV decryption failed"); ++ goto fail; ++ } ++ wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext", ++ unwrapped, unwrapped_len); ++ ++ if (dpp_check_attrs(unwrapped, unwrapped_len) < 0) { ++ dpp_auth_fail(auth, "Invalid attribute in unwrapped data"); ++ goto fail; ++ } ++ ++ e_nonce = dpp_get_attr(unwrapped, unwrapped_len, ++ DPP_ATTR_ENROLLEE_NONCE, ++ &e_nonce_len); ++ if (!e_nonce || e_nonce_len != auth->curve->nonce_len) { ++ dpp_auth_fail(auth, ++ "Missing or invalid Enrollee Nonce attribute"); ++ goto fail; ++ } ++ wpa_hexdump(MSG_DEBUG, "DPP: Enrollee Nonce", e_nonce, e_nonce_len); ++ os_memcpy(auth->e_nonce, e_nonce, e_nonce_len); ++ ++ config_attr = dpp_get_attr(unwrapped, unwrapped_len, ++ DPP_ATTR_CONFIG_ATTR_OBJ, ++ &config_attr_len); ++ if (!config_attr) { ++ dpp_auth_fail(auth, ++ "Missing or invalid Config Attributes attribute"); ++ goto fail; ++ } ++ wpa_hexdump_ascii(MSG_DEBUG, "DPP: Config Attributes", ++ config_attr, config_attr_len); ++ ++ root = json_parse((const char *) config_attr, config_attr_len); ++ if (!root) { ++ dpp_auth_fail(auth, "Could not parse Config Attributes"); ++ goto fail; ++ } ++ ++ token = json_get_member(root, "name"); ++ if (!token || token->type != JSON_STRING) { ++ dpp_auth_fail(auth, "No Config Attributes - name"); ++ goto fail; ++ } ++ wpa_printf(MSG_DEBUG, "DPP: Enrollee name = '%s'", token->string); ++ ++ token = json_get_member(root, "wi-fi_tech"); ++ if (!token || token->type != JSON_STRING) { ++ dpp_auth_fail(auth, "No Config Attributes - wi-fi_tech"); ++ goto fail; ++ } ++ wpa_printf(MSG_DEBUG, "DPP: wi-fi_tech = '%s'", token->string); ++ if (os_strcmp(token->string, "infra") != 0) { ++ wpa_printf(MSG_DEBUG, "DPP: Unsupported wi-fi_tech '%s'", ++ token->string); ++ dpp_auth_fail(auth, "Unsupported wi-fi_tech"); ++ goto fail; ++ } ++ ++ token = json_get_member(root, "netRole"); ++ if (!token || token->type != JSON_STRING) { ++ dpp_auth_fail(auth, "No Config Attributes - netRole"); ++ goto fail; ++ } ++ wpa_printf(MSG_DEBUG, "DPP: netRole = '%s'", token->string); ++ if (os_strcmp(token->string, "sta") == 0) { ++ ap = 0; ++ } else if (os_strcmp(token->string, "ap") == 0) { ++ ap = 1; ++ } else { ++ wpa_printf(MSG_DEBUG, "DPP: Unsupported netRole '%s'", ++ token->string); ++ dpp_auth_fail(auth, "Unsupported netRole"); ++ goto fail; ++ } ++ ++ resp = dpp_build_conf_resp(auth, e_nonce, e_nonce_len, ap); ++ ++fail: ++ json_free(root); ++ os_free(unwrapped); ++ return resp; ++} ++ ++ ++static struct wpabuf * ++dpp_parse_jws_prot_hdr(const struct dpp_curve_params *curve, ++ const u8 *prot_hdr, u16 prot_hdr_len, ++ const EVP_MD **ret_md) ++{ ++ struct json_token *root, *token; ++ struct wpabuf *kid = NULL; ++ ++ root = json_parse((const char *) prot_hdr, prot_hdr_len); ++ if (!root) { ++ wpa_printf(MSG_DEBUG, ++ "DPP: JSON parsing failed for JWS Protected Header"); ++ goto fail; ++ } ++ ++ if (root->type != JSON_OBJECT) { ++ wpa_printf(MSG_DEBUG, ++ "DPP: JWS Protected Header root is not an object"); ++ goto fail; ++ } ++ ++ token = json_get_member(root, "typ"); ++ if (!token || token->type != JSON_STRING) { ++ wpa_printf(MSG_DEBUG, "DPP: No typ string value found"); ++ goto fail; ++ } ++ wpa_printf(MSG_DEBUG, "DPP: JWS Protected Header typ=%s", ++ token->string); ++ if (os_strcmp(token->string, "dppCon") != 0) { ++ wpa_printf(MSG_DEBUG, ++ "DPP: Unsupported JWS Protected Header typ=%s", ++ token->string); ++ goto fail; ++ } ++ ++ token = json_get_member(root, "alg"); ++ if (!token || token->type != JSON_STRING) { ++ wpa_printf(MSG_DEBUG, "DPP: No alg string value found"); ++ goto fail; ++ } ++ wpa_printf(MSG_DEBUG, "DPP: JWS Protected Header alg=%s", ++ token->string); ++ if (os_strcmp(token->string, curve->jws_alg) != 0) { ++ wpa_printf(MSG_DEBUG, ++ "DPP: Unexpected JWS Protected Header alg=%s (expected %s based on C-sign-key)", ++ token->string, curve->jws_alg); ++ goto fail; ++ } ++ if (os_strcmp(token->string, "ES256") == 0 || ++ os_strcmp(token->string, "BS256") == 0) ++ *ret_md = EVP_sha256(); ++ else if (os_strcmp(token->string, "ES384") == 0 || ++ os_strcmp(token->string, "BS384") == 0) ++ *ret_md = EVP_sha384(); ++ else if (os_strcmp(token->string, "ES512") == 0 || ++ os_strcmp(token->string, "BS512") == 0) ++ *ret_md = EVP_sha512(); ++ else ++ *ret_md = NULL; ++ if (!*ret_md) { ++ wpa_printf(MSG_DEBUG, ++ "DPP: Unsupported JWS Protected Header alg=%s", ++ token->string); ++ goto fail; ++ } ++ ++ kid = json_get_member_base64url(root, "kid"); ++ if (!kid) { ++ wpa_printf(MSG_DEBUG, "DPP: No kid string value found"); ++ goto fail; ++ } ++ wpa_hexdump_buf(MSG_DEBUG, "DPP: JWS Protected Header kid (decoded)", ++ kid); ++ ++fail: ++ json_free(root); ++ return kid; ++} ++ ++ ++static int dpp_parse_cred_legacy(struct dpp_authentication *auth, ++ struct json_token *cred) ++{ ++ struct json_token *pass, *psk_hex; ++ ++ wpa_printf(MSG_DEBUG, "DPP: Legacy akm=psk credential"); ++ ++ pass = json_get_member(cred, "pass"); ++ psk_hex = json_get_member(cred, "psk_hex"); ++ ++ if (pass && pass->type == JSON_STRING) { ++ size_t len = os_strlen(pass->string); ++ ++ wpa_hexdump_ascii_key(MSG_DEBUG, "DPP: Legacy passphrase", ++ pass->string, len); ++ if (len < 8 || len > 63) ++ return -1; ++ os_strlcpy(auth->passphrase, pass->string, ++ sizeof(auth->passphrase)); ++ } else if (psk_hex && psk_hex->type == JSON_STRING) { ++ if (dpp_akm_sae(auth->akm) && !dpp_akm_psk(auth->akm)) { ++ wpa_printf(MSG_DEBUG, ++ "DPP: Unexpected psk_hex with akm=sae"); ++ return -1; ++ } ++ if (os_strlen(psk_hex->string) != PMK_LEN * 2 || ++ hexstr2bin(psk_hex->string, auth->psk, PMK_LEN) < 0) { ++ wpa_printf(MSG_DEBUG, "DPP: Invalid psk_hex encoding"); ++ return -1; ++ } ++ wpa_hexdump_key(MSG_DEBUG, "DPP: Legacy PSK", ++ auth->psk, PMK_LEN); ++ auth->psk_set = 1; ++ } else { ++ wpa_printf(MSG_DEBUG, "DPP: No pass or psk_hex strings found"); ++ return -1; ++ } ++ ++ if (dpp_akm_sae(auth->akm) && !auth->passphrase[0]) { ++ wpa_printf(MSG_DEBUG, "DPP: No pass for sae found"); ++ return -1; ++ } ++ ++ return 0; ++} ++ ++ ++static EVP_PKEY * dpp_parse_jwk(struct json_token *jwk, ++ const struct dpp_curve_params **key_curve) ++{ ++ struct json_token *token; ++ const struct dpp_curve_params *curve; ++ struct wpabuf *x = NULL, *y = NULL; ++ EC_GROUP *group; ++ EVP_PKEY *pkey = NULL; ++ ++ token = json_get_member(jwk, "kty"); ++ if (!token || token->type != JSON_STRING) { ++ wpa_printf(MSG_DEBUG, "DPP: No kty in JWK"); ++ goto fail; ++ } ++ if (os_strcmp(token->string, "EC") != 0) { ++ wpa_printf(MSG_DEBUG, "DPP: Unexpected JWK kty '%s'", ++ token->string); ++ goto fail; ++ } ++ ++ token = json_get_member(jwk, "crv"); ++ if (!token || token->type != JSON_STRING) { ++ wpa_printf(MSG_DEBUG, "DPP: No crv in JWK"); ++ goto fail; ++ } ++ curve = dpp_get_curve_jwk_crv(token->string); ++ if (!curve) { ++ wpa_printf(MSG_DEBUG, "DPP: Unsupported JWK crv '%s'", ++ token->string); ++ goto fail; ++ } ++ ++ x = json_get_member_base64url(jwk, "x"); ++ if (!x) { ++ wpa_printf(MSG_DEBUG, "DPP: No x in JWK"); ++ goto fail; ++ } ++ wpa_hexdump_buf(MSG_DEBUG, "DPP: JWK x", x); ++ if (wpabuf_len(x) != curve->prime_len) { ++ wpa_printf(MSG_DEBUG, ++ "DPP: Unexpected JWK x length %u (expected %u for curve %s)", ++ (unsigned int) wpabuf_len(x), ++ (unsigned int) curve->prime_len, curve->name); ++ goto fail; ++ } ++ ++ y = json_get_member_base64url(jwk, "y"); ++ if (!y) { ++ wpa_printf(MSG_DEBUG, "DPP: No y in JWK"); ++ goto fail; ++ } ++ wpa_hexdump_buf(MSG_DEBUG, "DPP: JWK y", y); ++ if (wpabuf_len(y) != curve->prime_len) { ++ wpa_printf(MSG_DEBUG, ++ "DPP: Unexpected JWK y length %u (expected %u for curve %s)", ++ (unsigned int) wpabuf_len(y), ++ (unsigned int) curve->prime_len, curve->name); ++ goto fail; ++ } ++ ++ group = EC_GROUP_new_by_curve_name(OBJ_txt2nid(curve->name)); ++ if (!group) { ++ wpa_printf(MSG_DEBUG, "DPP: Could not prepare group for JWK"); ++ goto fail; ++ } ++ ++ pkey = dpp_set_pubkey_point_group(group, wpabuf_head(x), wpabuf_head(y), ++ wpabuf_len(x)); ++ *key_curve = curve; ++ ++fail: ++ wpabuf_free(x); ++ wpabuf_free(y); ++ ++ return pkey; ++} ++ ++ ++int dpp_key_expired(const char *timestamp, os_time_t *expiry) ++{ ++ struct os_time now; ++ unsigned int year, month, day, hour, min, sec; ++ os_time_t utime; ++ const char *pos; ++ ++ /* ISO 8601 date and time: ++ * T